Delphiでスマートポインタを。
ティファニーで朝食を、みたいな感じで書いたつもりが全然元ネタの香りもしない感じになったのは置いとくとして、Delphiにジェネリクスが実装されたのでスマートポインタも実装し放題な今日この頃、というか時期的にはもう旬は終わった感もあるくらいなので、各地にはたくさんの実装がニョキニョキとすでに生まれてたりとか何とか。で、その中で一番シンプルで素敵だった実装(+Lynaたんによる改悪)がこちら。
unit Lyna.Generics; interface uses SysUtils; type TSmartPointer<T: class, constructor> = class(TInterfacedObject, TFunc<T>) private FValue: T; public constructor Create(AValue: T); overload; constructor Create; overload; destructor Destroy; override; function Invoke: T; property Value: T read FValue; end; implementation { TSmartPointer<T> } constructor TSmartPointer<T>.Create(AValue: T); begin inherited Create; FValue := AValue; end; constructor TSmartPointer<T>.Create; begin inherited; FValue := nil; end; destructor TSmartPointer<T>.Destroy; begin FValue.Free; inherited; end; function TSmartPointer<T>.Invoke: T; begin if not Assigned(FValue) then FValue := T.Create; Result := FValue; end; end.
例えばIDE上でTFunc
ちなみに拡張したところは、型パラメータにconstructor制約を課すことによって遅延生成対応にした部分。正直イラネと思われるかもしれないけど、ただでさえ以下のように記述量が多くなりがちなので、
var http: TFunc<TIdHTTP>; s: string; begin http := TSmartPointer<TIdHTTP>.Create(TIdHTTP.Create); // 'TIdHTTP'と'Create'を二度も書く必要があるなんて>< s := http.Get('http://twc.xrea.jp'); Memo1.Lines.Add(s); end;
次のようにすることでちょっとだけ記述を減らせる。まぁそれだけなんだけど。
var http: TFunc<TIdHTTP>; s: string; begin http := TSmartPointer<TIdHTTP>.Create(); // Createの後の()は省けないけど、上のよりはすっきり s := http.Get('http://twc.xrea.jp'); // ここで初めてTIdHTTPのインスタンスが生成される Memo1.Lines.Add(s); end;
ただしconstructor制約下では引数を取らないコンストラクタしか呼び出せないので、THoge.Create('hogehoge')と書いて生成しなければならないようなクラスは上の例のような書き方のみ使える*1。