9

デルファイの関数/手順から単純なオブジェクトを返すためのベストプラクティスは何ですか?

例えば。2種類のコード:

作成したオブジェクトを参照として渡し、Proc にオブジェクトを移入し、後で破棄します

procedure Proc(var Obj: TMyObject);
begin
  // populate Obj
end;

O := TMyObject.Create;
try
  Proc(O);
  // manipulate populated object
finally
  O.Free;
end;

または 、関数の結果として作成されたオブジェクトを取得し、操作後に破棄します

function Func: TMyObj;
begin
  Result := TMyObj.Create;
end;

O := Func;
if O <> nil then
begin
  try
    // manipulate
  finally
    O.Free;
  end;
end;
4

6 に答える 6

8

ベスト プラクティスはありません。ただし、最初に行うべきことは、例外が発生した場合でも、いつでもオブジェクトの破棄の責任者を常に明確にすることです。

関数が新しいインスタンスを作成して返すことに問題はありません。そのような関数はfactoryです。クラスのコンストラクターのように扱うことができるため、コンストラクターのように動作することを確認する必要があります。有効なオブジェクトを返すか、例外をスローします。null 参照を返すことはありません。

function Func: TMyObj;
begin
  Result := TMyObj.Create;
  try
    Result.X := Y;
  except
    Result.Free;
    raise;
  end;
end;

これはめったに見られない例外処理パターンですが、このスタイルの関数にとっては重要です。オブジェクトを返すと、所有権が関数から呼び出し元に転送されますが、それはそれが完全に実行された場合のみです。例外のために早期に終了する必要がある場合、呼び出し元はそれ自体を解放する方法がないため、オブジェクトを解放します。(例外により終了する関数には戻り値がありません。) 呼び出し元は次のように使用します。

O := Func;
try
  writeln(O.X);
finally
  O.Free;
end;

例外がある場合FuncO割り当てられないため、呼び出し元が解放できるものは何もありません。


呼び出し元がオブジェクトを作成し、それを別の関数に渡して初期化する場合、パラメーターを「var」パラメーターにしないでください。これにより、代わりに子孫型が作成された場合でも、関数によって要求された型とまったく同じ型の変数を使用する必要がある呼び出し元に特定の制限が課されます。

そのような関数はオブジェクトを解放すべきではありません。呼び出し元は、特に関数が返された後にオブジェクトを使用することを計画している場合、呼び出す関数に所有権の責任を与えません。

于 2009-11-08T20:14:21.277 に答える
4

それは、オブジェクトの存続期間とその責任者によって異なります。ほとんどの場合、オブジェクトは同じエンティティによって作成および破棄される必要があります。

メソッドが TStringList にファイルの解析結果を入力するとします。その関数で TStringList を作成する必要がありますか、それとも作成して参照として渡す必要がありますか?

それを作成し、参照として渡し、後で破棄することは、すべて連続したコード行で行う方が読みやすいと思います。

ここで、追加された顧客ごとに TCustomer を返す関数があるとします。その場合、関数を使用します。これは、私のエンティティには、必要のないときに顧客を破棄する責任がある顧客のリストまたは何かがあると思われるためです。

于 2009-11-08T18:19:13.687 に答える
3

呼び出し元にオブジェクトを作成させ、それをパラメーターとして渡すことは、一般的なDelphiのイディオムです。varほとんどの場合、宣言する必要はないことに注意してください。

procedure Proc (Obj : TMyObject)
begin
  Obj.SomeProperty := 'SomeValue';
  ...
end;

市外局番:

Obj := TMyObject.Create;
try
  Proc (Obj);
finally
  FreeAndNil (Obj);
end;    

これにより、誰がオブジェクトを解放する必要があるかについての混乱を回避できます。メソッド呼び出しのチェーンがある場合、ラインのどこかで解放する必要があるオブジェクトを追跡するのはすぐに非常に複雑になる可能性があることに注意してください。

もう1つの欠点:コード内に作成と破棄が散在していると、try...finallyブロックを使用できなくなります。これは、リソースリークを回避するためのもう1つの便利なイディオムです。

メソッドでオブジェクトを作成する場合は、関数名で明示的にします。これは、私にはぴったりのようにCreateAndInitializeList聞こえます。

于 2009-11-08T19:05:59.387 に答える
2

私のルールは、所有権と創造物を一緒に持つことです。私は常に作成者が所有者であるため、オブジェクトを破棄する責任があります。オブジェクトの作成は呼び出しコードで明示的に行われ、呼び出しの副作用ではありません。

したがって、私の関数の通常の署名は

function Func(o:tMyO): TMyO;
begin
  // ....
  Result := o;
end;

このようにして、私はどちらかを行うことができます

   o := func(TMyO.create);

また

  o := TMyO.create;
  // ...
  func(o);
于 2009-11-08T18:54:59.793 に答える
-3

コンストラクトをよく使う

FUNCTION SomeFunction(SL : TStrings = NIL) : TStrings;
  BEGIN
    IF Assigned(SL) THEN Result:=SL ELSE Result:=TStringList.Create;
    // Use Result for the remainder of the function
  END;

そうすれば、参照が渡された PROCEDURE としても、インスタンス自体を作成する FUNCTION としても使用できます。

于 2009-11-08T18:40:56.193 に答える