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

良い一日。

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

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構文をサポートするソースコードハイライターの類似物を勧めますか?

Source: https://habr.com/ru/post/J87692/


All Articles