nullableまたはブール値のreturn + outパラメーターを使用する方が良い

StackOverflow https://stackoverflow.com/questions/1800003

  •  05-07-2019
  •  | 
  •  

質問

ある整数値を返す必要がある関数があるとしましょう。失敗する可能性もあります。失敗するタイミングを知る必要があります。

より良い方法はどれですか

public int? DoSomethingWonderful()

または

public bool DoSomethingWonderful(out int parameter)

これはおそらくスタイルの質問ですが、私はまだどのオプションを採用するのか興味があります。

編集:明確化、このコードはブラックボックスと通信します(クラウドと呼びましょう。いいえ、ブラックボックス。失敗した理由はなぜ気にしません。有効な値があるかどうかを知る必要があるだけです。

役に立ちましたか?

解決

null合体演算子を使用できるので、null可能バージョンの方が好きですか?その上、例えば:

int reallyTerrible = 0;
var mightBeWonderful = DoSomethingWonderful() ?? reallyTerrible;

他のヒント

呼び出し元のコードがどのように見えるかによって異なります。したがって、関数の用途は。

一般に、out引数を避ける必要があります。一方、次のようなコードがあると便利です。

int parameter;
if (DoSomething(out paramameter))
{
  // use parameter
}

null可能なintがある場合、次のようになります。

int? result = DoSomething();
if (result != null)
{
  // use result
}

これは、out引数を持たないため、多少改善されますが、関数が成功したかどうかを決定するコードはあまり明確に見えません。

別のオプションがあることを忘れないでください:例外を使用します。これは、関数が失敗するケースが本当に例外的な種類のエラーケースである場合にのみ実行してください。

try
{
  // normal case
  int result = DoSomething()
}
catch (SomethingFailedException ex)
{
  // exceptional case
}

例外の利点の1つは、単に無視できないことです。通常の場合も実装は簡単です。例外的なケースが無視できる場合は、例外を使用しないでください。

編集:言及するのを忘れた:例外のもう1つの利点は、操作が失敗した理由 も提供できることです。この情報は、例外タイプ、例外のプロパティ、およびメッセージテキストによって提供されます。

例外をスローしない理由

.Netライブラリのある場所で使用されているパターンに従います:

bool int.TryParse(string s, out value)
bool Dictionary.TryGetValue(T1 key, out T2 value)

だから私は言うでしょう:

public bool TryDoSomethingWonderful(out int parameter)

それは本当にあなたが何をしているかに依存します。

nullは意味のある答えですか?そうでない場合は、 bool TryDoSomethingWonderful(out int)メソッド呼び出しを使用します。これはフレームワークと一致します。

ただし、nullが意味のある戻り値である場合、intを返しますか?理にかなっています。

パフォーマンスが主な関心事でない限り、 int を返し、失敗した場合に例外をスローする必要があります。

2番目のメソッドを使用します。呼び出しが成功したかどうかをすぐに知る必要があるためです。その場合は、次のように記述します

int x;
if( DoSomethingWonderful( out x ) )
{
    SomethingElse(x);
}

より

int? x = DoSomethingWonderful();
if( x.HasValue )
{
   SomethingElse(x.Value);
}

出力パラメーターを使用することに賛成です。私の意見では、これは出力パラメーターの使用が最も適しているような状況です。

はい、残りのコードで使用できる代替値がある場合にのみ、合体演算子を使用してコードをワンライナーとして保持できます。多くの場合、それは私には当てはまらず、値を正常に取得できた場合とは異なるコードパスを実行することを好みます。

        int value;
        if(DoSomethingWonderful(out value))
        {
            // continue on your merry way
        }
        else
        {
            // oops
            Log("Unable to do something wonderful");

            if (DoSomethingTerrible(out value))
            {
                // continue on your not-so-merry way
            }
            else
            {
                GiveUp();
            }
        }

さらに、取得したい値が実際にnull可能である場合、出力パラメータとブール値の戻り値を持つ関数を使用することは、私の意見では、" Iの失敗を区別する最も簡単な方法です値の取得"および「取得した値はnullです」。次の例のように、時々その区別が気になります:

    private int? _Value;
    private bool _ValueCanBeUsed = false;

    public int? Value
    {
        get { return this._Value; }
        set
        {
            this._Value = value;
            this._ValueCanBeUsed = true;
        }
    }

    public bool DoSomethingTerrible(out int? value)
    {
        if (this._ValueCanBeUsed)
        {
            value = this._Value;
            // prevent others from using this value until it has been set again
            this._ValueCanBeUsed = false;
            return true;
        }
        else
        {
            value = null;
            return false;
        }
    }

私の意見では、ほとんどの人が出力パラメーターを使用しない傾向がある唯一の理由は、構文が面倒だと感じるからです。しかし、出力パラメーターを使用する方がこの問題に対するより適切な解決策であると本当に感じており、慣れると、null値を返すよりも構文がはるかに望ましいことがわかりました。

失敗する可能性のある方法が1つしかない場合、または失敗する理由を知る必要がない場合は、null可能な戻り値を使用する方が簡単で簡単だと思います。

逆に、失敗する可能性のある方法が複数あり、呼び出し元のコードが失敗の正確な理由を知りたい場合は、outパラメーターを使用して、ブールではなくエラーコードを返します(または、例外をスローすることもできます、しかしあなたの質問に基づいて、あなたはすでに例外を投げないことに決めているようです)。

try catchを使用する必要があります。これは、発信者が何が起こるかわからないようです?

boolとoutの両方をチェックするか、nullと実際のリターンの両方をチェックする必要があります。

メソッドに必要な処理を行わせます。失敗した場合は、呼び出し元に失敗したことを通知し、呼び出し元は必要に応じて処理します。

興味深いことに、私の個人的な意見はメソッドの性質に大きく依存しています。具体的には、メソッドの目的が「何かをする」ことに反対して、単一の値を取得する場合です。

例:

bool GetSerialNumber(out string serialNumber)

vs

string GetSerialNumber() // returns null on failure

2番目は、より「自然」に感じます。どういうわけか、そして同様に:

bool GetDeviceId(out int id)

vs

int? GetDeviceId() // returns null on failure`

しかし、これは本当に「コーディングスタイル」に該当すると認めます。領土。

ああ、私も例外のスローを好む傾向があります:

int GetDeviceId() // throws an exception on read failure

私は、なぜ彼らがそんなに間違っているのか、まだ売れていません。 Orenにスレッドを付けることはできますか? ;-)

Microsoftの" Try"が嫌いです。 「アウト」が発生するパターンパラメータは、データ項目を返すために使用されます。とりわけ、その方法でコーディングされたメソッドは、共変インターフェイスでは使用できません。私はむしろ T GetValue(out bool Successful)またはおそらく T GetValue(out GetValueErrorEnum result); または T GetValue(out GetValueErrorInfo result ); true / falseを超えるものが必要な場合。すべてのデータ型には有効なデフォルト値があるため、関数が失敗した場合に何を返すかを決定するのに問題はありません。呼び出しコードは簡単に言うことができます:

  bool success;
  var myValue = Thing.GetValue(ref success);
  if (success)
    .. do something using myValue
  else
    .. ignore myValue

.netとC#が真の共変「コピーアウト」パラメーターを提供した場合は良いでしょう(呼び出し側は結果にスペースを割り当て、そのスペースへのポインターを呼び出された関数に渡し、割り当てられたスペースを渡された関数にコピーします-in変数は関数が返された後のみ)。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top