2017-02-15 19 views
3

私はいくつかのパッケージを含むプロジェクトに取り組んでいます。私の基本パッケージの一つで、私は(ここでは完全なコードがある)そのようなスマートポインタを宣言:Delphi - スマートポインタコンストラクタでの不思議な動作

unit UTWSmartPointer; 

interface 

type 
    IWSmartPointer<T> = reference to function: T; 

    TWSmartPointer<T: class, constructor> = class(TInterfacedObject, IWSmartPointer<T>) 
    private 
     m_pInstance: T; 

    public 
     constructor Create; overload; virtual; 

     constructor Create(pInstance: T); overload; virtual; 

     destructor Destroy; override; 

     function Invoke: T; virtual; 
    end; 

implementation 
//--------------------------------------------------------------------------- 
constructor TWSmartPointer<T>.Create; 
begin 
    inherited Create; 

    m_pInstance := T.Create; 
end; 
//--------------------------------------------------------------------------- 
constructor TWSmartPointer<T>.Create(pInstance: T); 
begin 
    inherited Create; 

    m_pInstance := pInstance; 
end; 
//--------------------------------------------------------------------------- 
destructor TWSmartPointer<T>.Destroy; 
begin 
    m_pInstance.Free; 
    m_pInstance := nil; 

    inherited Destroy; 
end; 
//--------------------------------------------------------------------------- 
function TWSmartPointer<T>.Invoke: T; 
begin 
    Result := m_pInstance; 
end; 
//--------------------------------------------------------------------------- 

end. 

その後、私のプロジェクトで(別のパッケージに)、私は(GDI +オブジェクトと、このスマートポインタを使用しますTGpGraphicsPath)。私は、コードを実行するとき

... 
pGraphicsPath: IWSmartPointer<TGpGraphicsPath>; 
... 
pGraphicsPath := TWSmartPointer<TGpGraphicsPath>.Create(); 
... 

しかし、何も画面上に描画されていない:私はそのようなグラフィックのパスを宣言します。私はエラーも例外もアクセス違反もなく、空白のページしか得られません。しかし、私はちょうどそのような私のコードを変更した場合:

... 
pGraphicsPath: IWSmartPointer<TGpGraphicsPath>; 
... 
pGraphicsPath := TWSmartPointer<TGpGraphicsPath>.Create(TGpGraphicsPath.Create); 
... 

は、すべての罰金となり、そして予想通り、私のパスが正確に描かれています。しかし、私は最初のコンストラクタが期待通りに動作しない理由を理解できません。誰かが私にこの奇妙な行動を説明することができますか?

よろしくお願いいたします。

+3

をTGpGraphicsPath.Create(optionalArgument) - ジェネリックで注意する必要があるもの。 – Dsm

+1

@Remy匿名メソッドは実際にはインターフェイスであり、その実装の詳細はこのように広く使用されていることが判明しました:http://stackoverflow.com/a/39955320 –

+0

@Dsm 'TGpGraphicsPath'はコンストラクタにパラメータを持ちません。 –

答えて

4

これはかなり複雑なトラップです。あなたが書くとき:

TGpGraphicsPath.Create 

あなたはあなたがパラメータなしのコンストラクタを呼んでいると思うかもしれません。しかしそうではありません。実際には、このコンストラクタを呼び出しています。

constructor Create(fillMode: TFillMode = FillModeAlternate); reintroduce; overload;  

デフォルト値はコンパイラによって指定されています。

あなたが書いたあなたのスマートポインタクラスで:

T.Create 

これは本当にパラメータなしのコンストラクタを呼び出しています。しかしそれはTObjectによって定義されたコンストラクタです。そのコンストラクターを使用すると、TGPGraphicsPathインスタンスが正しく初期化されません。

constructorジェネリック制約を使用する場合は、パラメータのないコンストラクタで正しく構築できるクラスを必ず使用するようにする必要があります。あなたのために残念なことにTGPGraphicsPathが請求書に適合しません。確かにそのようなクラスの優勢があります。

コンストラクタを明示的に呼び出すのを避けるために、ここで行うことができることはまったくありません。あなたのスマートポインタクラスが、この特定のクラスのためにどのコンストラクタを呼び出すかは、ほとんど不可能です。

私の助言は、constructor一般的な制約から離れてスマートポインタクラスの消費者にインスタンスを明示的にインスタンス化することです。

これはかなり一般的な問題である - 私は一週間前より小さく、ここで同様の質問答え:私の推測はTGpGraphicsPathがTObject.Createが代わりに呼び出されます。その場合には、オプションの引数を取ることになりWhy does a deserialized TDictionary not work correctly?

+0

ジェネリックス/制約の元々の開発者が非常に厄介な間違いをして、ctorsが自動的に継承されるC#からそれらをコピーします。 ctor制約を使用することは意味があり、実際には祖先にはパラメータがないかもしれないが、パラメータのないctorを持たないクラスを使用することはできません。 Delphiでは、コンパイラは、子クラスがオーバーロードが追加されたctorsを持つとすぐにTObject ctorを認識します。しかし、今、私はこれを書いたことがあります。これは、コンパイラが適切なオーバーロード解決を行うことで修正できると思います。なぜなら、これらの場合にTObjectを表示してはならないからです。 –

+0

@stefan基本的な設計が大幅に変更されていない限り、コンパイラはパラメータなしのコンストラクタとしてデフォルトのargを持つ1つのparamコンストラクタを扱うことはできないと思います。 –

+0

コンパイラがその型の使用可能なコンストラクタを単に見ることができない理由がわかりません。パラメータなしのコンストラクタが見つかった場合は、すべてデフォルトのパラメータを持つコンストラクタを探します。ユーザーコードはいずれの方法でも同じですが、これは通常ジェネリックスの外部で正常に動作します。そのため、コンパイラは、コードが汎用であるかどうかにかかわらず適切な処理を行う必要があります。この変更は、その定義がユーザのコードによって*入力パラメータを必要としないコンストラクタを少し含むように緩められている場合、 'constructor'制約を破るべきではありません。 –

関連する問題