9

この場合の説明が必要です。

私のテストによると、Result変数は、最初の行からBoolean = False、Integer = 0、String =''、Object=nilなどに定義されています。しかし、私はこれについての公式の参照を見たことがありません。これがヒントを与えるので、それも理にかなっています。

[DCC警告]Unit1.pas(35):「TForm1.Test」に割り当てられたH2077値は使用されませんでした

function TForm1.Test: Boolean;
begin
  Result := False;
  // Some arbitrary code here

  Result := True;
end;

しかし、最初の行をコメントアウトし、最後の行の前のどこかに例外がある場合はどうなりますか?結果=Falseですか?

Resultが未定義の場合、これは、後で例外が発生した場合に備えて、常にResultを定義してすべての関数を開始する必要があることを意味します。そして、これは私には意味がありません。

4

3 に答える 3

16

Delphiの公式ドキュメントに記載されているように、結果は次のいずれかになります。

  • レジスタに含まれる順序値および要素のCPUレジスタ(AL / AX / EAX / RAX / EAX:EDX)。
  • FPUレジスタ(st(0)/ XMM1);
  • 最新のパラメーターとして渡される追加の変数。

原則として、デフォルトでは結果値は定義されていません。設定する必要があります。コンパイラーは、欠落している結果セットについて警告します。

文字列、動的配列、メソッドポインター、またはバリアント結果の場合、効果は、関数の結果が宣言されたパラメーターに続く追加のvarパラメーターとして宣言された場合と同じです。つまり、呼び出し元は、関数の結果を返す変数を指す追加の32ビットポインターを渡します。

正確には、varパラメーターは管理対象タイプだけでなく、呼び出し前にスタックに割り当てられた結果recordまたは結果のみを対象としているため、同じ動作が適用されます。object

つまり、たとえば、結果がである場合、それは追加のパラメーターstringとして渡されます。varしたがって、デフォルトでは、呼び出し前の値が含まれます。最初はそうなり''ますが、関数を数回呼び出すと、前の値が含まれます。

function GetString: string;
// is compiled as procedure GetString(var result: string);
begin
  if result='' then
    result := 'test' else
    writeln('result=',result);
end;

function GetRaise: string;
// is compiled as procedure GetRaise(var result: string);
begin
  result := 'toto';
  raise Exception.Create('Problem');
end;

var s: string;
begin
  // here s=''
  s := GetString; // called as GetString(s);
  // here s='test'
  s := GetString; // called as GetString(s);
  // will write 'result=test' on the console
  try
    s := GetRaise; // called as GetRaise(s);
  finally
    // here s='toto'
  end;
end;

だから私のアドバイスは:

  • 未設定の結果に関するすべてのコンパイラ警告を修正しました。
  • 結果文字列が''に初期化されると想定しないでください(最初は可能ですが、2回目の呼び出しではそうではありません)-これはvarパラメーターとしてではなく、パラメーターとして渡されoutます。
  • いずれも通常どおりexceptionに処理されます。つまり、実行中のフローは次のフローまたはブロックにジャンプします。ただし、結果がパラメーターとして送信され、何かが既に割り当てられている場合は、値が設定されます。finallyexceptvarresult
  • ほとんどの場合、未設定の結果の順序値(ブール値など)が0であるためではなく(リターン直前のasmコードでEAX = 0であるため)、次回になります(顧客側でランダムな問題が発生しました)そのような未設定の結果変数のために:それはほとんどの場合機能し、それから時々コードが失敗します...);
  • 新しいバージョンのDelphiでは、構文を使用しexit()て値を返すことができます。
于 2012-04-10T07:23:46.227 に答える
10

あなたは次のように述べています。

Resultが未定義の場合、これは、後で例外が発生した場合に備えて、常にResultを定義してすべての関数を開始する必要があることを意味します。

関数が例外を発生させた場合、関数の戻り値が未定義になることが懸念されます。しかし、それは問題ではありません。次のコードを検討してください。

x := fn();

関数の本体でfn例外が発生した場合は、呼び出しサイトに戻って、xに割り当てる必要はありません。論理的には、上記のワンライナーはツーライナーと考え​​ることができます。

  1. 電話fn()
  2. に戻り値を割り当てるx

1行目で例外が発生した場合、2行目は発生しxないため、割り当てる必要はありません。

したがって、割り当てる前に例外が発生したResult場合でも、関数が例外を発生させた場合に関数の戻り値を使用してはならないため、これは問題にはなりません。


実際に気にする必要があるのは、関連する問題です。に割り当てた、例外が発生した場合はどうResultなりますか?割り当てた値が関数の外に伝播する可能性はありますか?悲しいことに、答えはイエスです。Result

Result多くの結果タイプ(整数、ブールなど)の場合、関数が例外を発生させた場合、割り当てた値は関数の外部に伝播されません。ここまでは順調ですね。

ただし、一部の結果タイプ(文字列、動的配列、インターフェイス参照、バリアントなど)には、問題を複雑にする実装の詳細があります。戻り値はvarパラメーターとして関数に渡されます。そして、関数の外部から戻り値を初期化できることがわかりました。このような:

s := 'my string';
s := fn();

の本体fnが実行を開始するとResult、値はになります'my string'。これは、次のようfnに宣言されているかのようです。

procedure fn(var Result: string);

Resultこれは、関数が後で例外を発生させた場合でも、変数に割り当てて、呼び出しサイトで変更を確認できることを意味します。それを回避するためのクリーンな方法はありません。できる最善の方法は、関数内のローカル変数に割り当て、関数の最後の動作としてResultのみを割り当てることです。

function fn: string;
var
  s: string;
begin
  s := ...
  ... blah blah, maybe raise exception
  Result := s;
end;

ここでは、Cスタイルのステートメントの欠如がreturn強く感じられます。


どのタイプの結果変数が上記の問題の影響を受けやすいかを正確に述べることは驚くほど困難です。当初、この問題は管理対象タイプに影響するだけだと思いました。しかし、Arnaudはコメントの中で、レコードとオブジェクトも影響を受けると述べています。そうですね、レコードまたはオブジェクトがスタック割り当てされている場合はそうです。それがグローバル変数であるか、ヒープが割り当てられている場合(たとえば、クラスのメンバー)、コンパイラーはそれを異なる方法で処理します。ヒープ割り当てレコードの場合、暗黙のスタック割り当て変数を使用して関数の結果を返します。関数が戻ったときにのみ、これはヒープに割り当てられた変数にコピーされます。したがって、呼び出しサイトで関数の結果変数を割り当てる値は、関数自体のセマンティクスに影響します。


私の意見では、これはすべて、言語設計において、関数の戻り値がvarセマンティクスではなくセマンティクスを持つことが恐ろしい間違いであった理由を非常に明確に示していoutます。

于 2012-04-10T07:00:43.403 に答える
6

いいえ、Result(保証された)デフォルト値はありません。値を指定しない限り、未定義です。これは、次のように記載されているドキュメントによって暗示されています

Resultまたは関数名に値を割り当てずに関数が終了した場合、関数の戻り値は未定義です。

試してみました

function test: integer;
begin
  ShowMessage(IntToStr(result));
end;

テキスト付きのメッセージを受け取りました35531136

于 2012-04-10T06:40:23.060 に答える