11

関数を呼び出して値を取得するときは、通常、関数が失敗するか何も返さない場合に備えて、変数を初期化し、初期化されていない変数の処理を避けたいと考えています。文字列、整数、またはその他の型についても同じことを行います。

整数変数の例:

vPropValue := 0;
vPropValue := GetPropValue(vObject,'Height');

IF vPropValue > 0 Then
...

これは私がそれを使用する最も一般的な方法です。

私は使用できることを知っています:

If GetPropValue(vObject,'Height') > 0 Then
...

ただし、最初の例では、コードの後半で結果が必要な場合に、関数を複数回呼び出すことを避けています。

文字列についても同じです(ローカル文字列が空の文字列に初期化されることはわかっていますが、整数は値を保持できません)

vName := '';
vName := GetObjectName(vObject,'ObjectName');

IF Trim(vPropStrValue) <> '' Then
...

すべてが失敗した場合に Function が 0 を返すようにするなど、重複した値の割り当てを回避するための措置を講じることができることはわかっています。しかし、私は何百もの関数を持っていますが、信頼できません。関数がすべてを処理する方法を間違えたことはありません。すべてが失敗した場合、0 を返さないものもあると確信しています。

これが望ましくない理由と、それを回避する最善の方法を理解しようとしています。

編集

関数が適切な値または 0 を返さない例を次に示します。

function GetValue(vType:integer):integer;
begin
   if vType=1 then
    Result:=100
   else if (vType>2) and (vType<=9) then
     Result:=200;

end;

procedure TForm1.Button1Click(Sender: TObject);
var vValue:integer;
begin

  vValue:=GetValue(11);
  Button1.Caption:=IntToStr(vValue);

end;

この場合、関数から返される値は乱数です。

この場合、初期化は有効なアプローチのようです。か否か?

編集2:

デビッドが彼の答えで指摘したように、正しい、警告がありました

[dcc32 Warning] Unit1.pas(33): W1035 Return value of function 'GetValue' might be undefined

しかし、私は理由もなくそれを無視しました。コンパイルさせてくれたので、大丈夫だと思いました。そのため、警告を探して、同様の問題があったかなりの数の関数を「修正」しました。これは、すべての IF Result が定義されていない可能性があるためです。

編集 3 & 結論:

質問と説明の範囲に追加されることを願っています:

ほとんどの関数で使用する別のひねりの例は、変数の初期化が必要だと思った理由を説明するかもしれません. . それらのほとんどはまだ次のように設定されています。

function GetProperty(vType:integer):integer;
begin
  Try
    if vType = 99 then
      Result:=GetDifferentProperty(vType)// <-- Call to another Function, that could return whatever...
    else
    begin
       if vType=1 then
          Result:=100
       else if (vType>2) and (vType<=9) then
         Result:=200;
    end;
  except
  end;
end;

今、私はこれらに対処してTry Except End;いますが、一部の機能は 10 年前のものであり、当時の私の経験に基づいて 100% 動作すると期待することは、信頼できるものではありません。

このプロジェクトの唯一の開発者として、私は自分の機能 (および残りのコード) を信頼すべきだと思いますが、複数の開発者環境ですべての機能が適切にセットアップされているとは想像できません。

だから私の結論:私は基本を気にしなかったので、適切に設計された関数、これらすべてのチェック(変数の初期化、Try Except行..)とおそらく他の不要なものを行う必要があります。

4

4 に答える 4

4

次のコード (A)

vPropValue := 0;
vPropValue := GetPropValue(vObject,'Height');

(B)と見分けがつかない

vPropValue := GetPropValue(vObject,'Height');

GetPropValue「正しく書かれている」かどうかは全く関係ありません。

GetPropValue間違って書いたとしてもどうなるか考えてみましょう。

function GetPropValue(AObject: TObject; AStr: String): Integer;
begin
  if AStr = 'Hello' then Result := 5;
end;

ご存じのとおり、入力AStrが「Hello」以外の場合、関数の結果はほぼランダムになります。(議論のために、それが戻ると仮定しましょう-42。)

コード ブロック (A) は次のことを行います。

  • vPropValue0 に設定
  • 次にvPropValue-42に設定します

コード ブロック (B) は、vPropValueただちに - 42 に設定されます。

ヒント: 呼び出した関数で間違いを犯したのではないかと心配しているからといって、無駄なコード行を書いても意味がありません。
まず、David が指摘しているように、コンパイラのヒントと警告に注意を払うだけで、多くの間違いを避けることができます。第二に、この種の「パラノイア」コーディングは無駄なコードにつながるだけです。ある日、「安全な値」が実際に有効な値
になると、これはさらに悪化します。たとえば、「デフォルト 0」と「正しく返された 0」の違いはどのようにわかりますか?

不必要な冗長性でコードを肥大化させて、人為的にプログラミングを難しくしないでください。


サイドノート

コードの動作が異なる特殊な状況がいくつかあります。ただし、コードの保守がはるかに困難になるため、いずれにしても、そのような状況につながる設計は避ける必要があります。

私は完全を期すためにそれらについて言及していますが、上記のアドバイスは依然として有効です。

1)vPropValueがプロパティとして実装されている場合、セッターは異なる動作を引き起こす副作用を持つ可能性があります。プロパティに問題はありませんが、予期しないことを行うと、深刻な問題が発生します。

2)vPropValueがクラスのフィールド (またはさらに悪いのはグローバル変数) の場合、(A) と (B)異なる動作をする可能性がありますが、例外が発生した場合のみです。 GetPropValueこれは、例外によって結果が割り当てられなくなるためです。これは、コードが何を行っているかを判断するのが難しくなるため、特別な場合を除いて避けるべきものです。

実際、これは、冗長な初期化を回避することが非常に重要になるものです。特別なケースのコードを他のコードより目立たせたいとします。

于 2015-11-26T08:37:00.803 に答える
2

アサーションを使用して、入力が正しいかどうかを検証できます。アサーションは、コード内の論理エラーを見つけるのに大いに役立ちます。

アサーションを使用すると、有効な入力データと無効な入力データについて考える必要があり、より良いコードを書くのに役立ちます。コンパイル時のアサーションの有効化と無効化は、\$C または \$ASSERTIONS コンパイラ (グローバル スイッチ) を介して行われます。

あなたの例では、関数 GetValue は次のようにアサーションを使用できます。

function GetValue(vType:integer):integer;
begin
   Assert((vType >= 1) and (vType <=9), 'Invalid input value in function GetValue');

   if vType=1 then
    Result:=100
   else if (vType>2) and (vType<=9) then
     Result:=200
   else Result := 0; // always catch the last else!
end;

さらに、すべての if ステートメントは最後の else をキャッチする必要があります。(私の意見では、常に!)

于 2015-11-27T09:24:34.513 に答える