あなたは次のように述べています。
Resultが未定義の場合、これは、後で例外が発生した場合に備えて、常にResultを定義してすべての関数を開始する必要があることを意味します。
関数が例外を発生させた場合、関数の戻り値が未定義になることが懸念されます。しかし、それは問題ではありません。次のコードを検討してください。
x := fn();
関数の本体でfn
例外が発生した場合は、呼び出しサイトに戻って、x
に割り当てる必要はありません。論理的には、上記のワンライナーはツーライナーと考えることができます。
- 電話
fn()
- に戻り値を割り当てる
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
ます。