デルファイ。 メソッドの終了時のオブジェクトの自動破棄

良い一日。



私はすべてのデルフィアンがこのコードを何千回も書いたと思う:



var

MyObj : TMyObj ;

begin

MyObj := TMyObj . Create ;

try

MyObj . DoWork ; // MyObj

finally

MyObj . Free ;

end ;

end ;









このようにしましょう。このような落書きはわずらわしいです。 関数/プロシージャを終了するときに、オブジェクト自体を破棄する必要があります。

まあ、これは実装するのに十分簡単です。 その結果、次のコードのようなものが得られます。

var

MyObj : TMyObj ;

begin

MyOb := CreateObjectDestroyer(TMyOb . Create) . ObjectAsPtr ;

MyObj . DoWork ; // MyObj

end ; // MyObj









以下に、この動作を実装する方法を説明します。





以下のすべてのコードは、インターフェースを備えたイルカのすべてのバージョンで機能することをすぐに指摘します(私の意見では、3番目から)。



インターフェイスで知られているように、Delphiは自動参照カウントを維持します。



var

Intf : IUnknown ;

begin

Intf := TInterfacedObject . Create ; // IUnknown.AddRef

//

end ; // IUnknown.Release









次に、TInterfacedObjectの下位クラスを作成します。これは、デストラクタで渡されたオブジェクトを破棄します。 それに、オブジェクトに小さな追加のインターフェイスを実装します。



type

IObjectDestroyer = interface (IInterface)

[ '{4DE81104-08B2-4821-960E-8935AC9B8F5E}' ]

function GetObjectAsPtr : Pointer ;

property ObjectAsPtr : Pointer read GetObjectAsPtr ; // ,

end ;



type

TObjectDestroyer = class (TInterfacedObject , IObjectDestroyer)

strict private

FObject : TObject ;

protected

function GetObjectAsPtr : Pointer ;

public

constructor Create (AObject : TObject ) ;

destructor Destroy ; override ;

end ;



constructor TObjectDestroyer . Create (AObject : TObject ) ;

begin

inherited Create() ;

FObject := AObject ;

end ;



destructor TObjectDestroyer . Destroy ;

begin

FObject . Free ;

inherited Destroy ;

end ;



function TObjectDestroyer . GetObjectAsPtr : Pointer ;

begin

Result := FObject ;

end ;









これで、次のようにオブジェクトを操作できます。



var

Destroyer : IObjectDestroyer ;

begin

Destroyer := TObjectDestroyer . Create(TMyObj . Create) ;

TMyObj(Destroyer . ObjectAsPtr) . DoWork() ;

end ;









MyObjを明示的に破棄する必要はありませんでしたが、TMyObj(Destroyer.ObjectAsPtr)を記述する必要があるため、それを控えめに書くことはできません。 それでは、追加の機能について説明しましょう。



function CreateObjectDestroyer (AObject : TObject ) : IObjectDestroyer ;

begin

Result := TObjectDestroyer . Create(AObject) ;

end ;









そして、一般的に、不要なDestroyer変数をコードから削除します。



var

MyObj : TMyObj ;

begin

MyOb := CreateObjectDestroyer(TMyOb . Create) . ObjectAsPtr ;

MyObj . DoWork ;

end ;









CreateObjectDestroyerを呼び出して、変数を暗黙的に作成します(前のバージョンでDestroyerとして説明したもの)。



さて、実際には問題は解決しましたが、このアプローチのいくつかの明白でない瞬間と率直な欠点に言及したいと思います。



最初はタイピングです。 より正確には、その不在。 ObjectAsPtrプロパティは、ポインターとして明示的に宣言されているため、任意のクラスの変数に割り当てることができます。



var

Obj1 : TMyObj1 ;

Obj2 : TMyObj2 ;

begin

Obj1 := CreateObjectDestroyer(TMyObj1 . Create) . ObjectAsPtr ;

Obj2 := CreateObjectDestroyer(TMyObj2 . Create) . ObjectAsPtr ;

end ;









ただし、この場合、コンパイラは型を監視せず、混乱する可能性があります。

Obj1 := CreateObjectDestroyer(TMyObj2 . Create) . ObjectAsPtr ;







この場合、おそらくアクセス違反を待っています



AObject:TObjectプロパティをIObjectDestroyerインターフェイスに追加し、型を明示的にキャストできます。

Obj1 := CreateObjectDestroyer(TMyObj2 . Create) . AObject as TMyObj1 ;









この場合、エラーはより明白になりますが、それでも実行時チェックです。 2009年バージョンよりも若いデルフォスのコンプ時間では、この問題は解決されず、ジェネリックは古いバージョンで登場しました。そこで入力を犠牲にすることはできません。



2番目の問題:オブジェクトの暗黙的な破壊の瞬間。



理論的には、コンパイラはCreateObjectDestroyerを呼び出した直後にReleaseを呼び出すことができます

Obj:= CreateObjectDestroyer(TMyObj.Create).ObjectAsPtr;

// ZarelizinインターフェイスとMyObjオブジェクトが破棄されるため、Objリンクは無効です。



ただし、 Barry Kellyは、この動作は将来変更されないと述べています。 ここでの議論を読むことができます



繰り返しますが、2009/2010 dolphの類型化問題と同様に、この欠点は部分的に解決できます。



さて、一般的には、これですべてです。この投稿が気に入った場合は、イルカの古いバージョンで同様のメカニズムをより透過的に実装する方法を説明します。



PS Dolphin構文をサポートするソースコードハイライターの類似物を勧めますか?



All Articles