web-dev-qa-db-ja.com

戻り値と出力パラメータのどちらが良いですか?

メソッドから値を取得する場合は、次のようにいずれかの戻り値を使用できます。

public int GetValue(); 

または:

public void GetValue(out int x);

私はそれらの違いを本当に理解していないので、どちらが良いかわかりません。これを説明してもらえますか?

ありがとうございました。

139
Vimvq1987

戻り値はほとんどalwaysです。メソッドに返すものがない場合は正しい選択です。 (実際、​​everを選択した場合、outパラメータを持つvoidメソッドが必要になるケースは考えられません。C#7のDeconstruct言語でサポートされている分解のメソッドは、このルールの非常にまれな例外として機能します。)

他のことは別として、呼び出し元が変数を個別に宣言する必要がなくなります。

int foo;
GetValue(out foo);

int foo = GetValue();

Out値は、次のようなメソッドチェーンも防ぎます。

Console.WriteLine(GetValue().ToString("g"));

(実際、それはプロパティセッターの問題の1つでもあり、ビルダーパターンがビルダーを返すメソッドを使用する理由です(例:myStringBuilder.Append(xxx).Append(yyy)。))

さらに、出力パラメータはリフレクションで使用するのが少し難しく、通常はテストも難しくなります。 (通常、出力パラメータよりも戻り値のモックを作成しやすくするためにより多くの労力が費やされます)。基本的に、彼らが作ると思うことは何もありませんeasier ...

戻り値FTW。

編集:何が起こっているの面で...

基本的に、「out」パラメータの引数を渡すと、変数を渡すhaveになります。 (配列要素も変数として分類されます。)呼び出すメソッドには、パラメーターのスタックに「新しい」変数がありません-変数をストレージに使用します。変数の変更はすぐに表示されます。違いを示す例を次に示します。

using System;

class Test
{
    static int value;

    static void ShowValue(string description)
    {
        Console.WriteLine(description + value);
    }

    static void Main()
    {
        Console.WriteLine("Return value test...");
        value = 5;
        value = ReturnValue();
        ShowValue("Value after ReturnValue(): ");

        value = 5;
        Console.WriteLine("Out parameter test...");
        OutParameter(out value);
        ShowValue("Value after OutParameter(): ");
    }

    static int ReturnValue()
    {
        ShowValue("ReturnValue (pre): ");
        int tmp = 10;
        ShowValue("ReturnValue (post): ");
        return tmp;
    }

    static void OutParameter(out int tmp)
    {
        ShowValue("OutParameter (pre): ");
        tmp = 10;
        ShowValue("OutParameter (post): ");
    }
}

結果:

Return value test...
ReturnValue (pre): 5
ReturnValue (post): 5
Value after ReturnValue(): 10
Out parameter test...
OutParameter (pre): 5
OutParameter (post): 10
Value after OutParameter(): 10

違いは「ポスト」ステップです。つまり、ローカル変数またはパラメーターが変更された後です。 ReturnValueテストでは、これは静的value変数に違いをもたらしません。 OutParameterテストでは、value変数はtmp = 10;行によって変更されます

150
Jon Skeet

より良いことは、あなたの特定の状況に依存します。 Oneoutが存在する理由の1つは、1つのメソッド呼び出しから複数の値を返すことを容易にするためです。

public int ReturnMultiple(int input, out int output1, out int output2)
{
    output1 = input + 1;
    output2 = input + 2;

    return input;
}

したがって、定義上、一方が他方より優れているわけではありません。ただし、通常は、たとえば上記の状況がない限り、単純なリターンを使用する必要があります。

EDIT:これは、キーワードが存在する理由の1つを示すサンプルです。上記はベストプラクティスと見なされるべきではありません。

26
pyrocumulus

通常、出力パラメータよりも戻り値を優先する必要があります。 2つのことを行う必要があるコードを記述していることに気づいた場合、out paramsは必需品です。これの良い例は、Tryパターン(Int32.TryParseなど)です。

2つのメソッドの呼び出し元が何をしなければならないかを考えてみましょう。最初の例では、これを書くことができます...

int foo = GetValue();

変数を宣言し、メソッドを介して1行で割り当てることができます。 2番目の例では、次のようになります...

int foo;
GetValue(out foo);

変数を前もって宣言し、2行でコードを書くことを余儀なくされました。

update

これらのタイプの質問をするときに調べるのに適した場所は、.NET Framework設計ガイドラインです。書籍版をお持ちの場合は、Anders Hejlsbergなどによるこの件に関する注釈(ページ184〜185)を見ることができますが、オンライン版はこちらです...

http://msdn.Microsoft.com/en-us/library/ms182131(VS.80).aspx

APIから2つのものを返す必要がある場合、それらを構造体/クラスにラップする方が、出力パラメーターよりも優れています。

23
Martin Peck

まだ言及されていないoutパラメーターを使用する理由が1つあります。呼び出しメソッドはそれを受信する義務があります。メソッドが、呼び出し側が破棄してはならない値を生成する場合、それをoutにすると、呼び出し側は強制的に明示的にそれを受け入れるようになります。

 Method1();  // Return values can be discard quite easily, even accidentally

 int  resultCode;
 Method2(out resultCode);  // Out params are a little harder to ignore

もちろん、呼び出し元はoutパラメーター内のvalueを無視できますが、あなたはそれに注意を呼びかけました。

これはまれなニーズです。多くの場合、真の問題に例外を使用するか、「FYI」の状態情報を含むオブジェクトを返す必要がありますが、これが重要な状況もあります。

12
user565869

主に好みです

返品を希望します。複数の返品がある場合は、結果DTOにラップできます

public class Result{
  public Person Person {get;set;}
  public int Sum {get;set;}
}
8
Scott Cowan

ほとんど常に戻り値を使用する必要があります。 'out'パラメーターは、多くのAPI、構成性などに多少の摩擦をもたらします。

頭に浮かぶ最も注目に値する例外は、TryParseパターンなど、複数の値を返す場合です(4.0までは.Net Frameworkにタプルがありません)。

5
Brian

戻り値は1つしか持てませんが、出力パラメーターは複数持つことができます。

これらの場合にのみ、パラメータを考慮する必要があります。

ただし、メソッドから複数のパラメーターを返す必要がある場合は、おそらくOOアプローチから何を返しているのかを見て、オブジェクトまたは構造体を返す方が良いかどうかを検討する必要があります。これらのパラメータを使用します。したがって、再び戻り値に戻ります。

5
Robin Day

この単純な例のいずれかではなく、以下を好むでしょう。

public int Value
{
    get;
    private set;
}

しかし、それらはすべて同じです。通常、メソッドから複数の値を渡す必要がある場合にのみ「out」を使用します。メソッドの内外に値を送信する場合は、「ref」を選択します。値を返すだけの場合は私の方法が最適ですが、パラメータを渡して値を取得する場合は、おそらく最初の選択肢を選択します。

2
kenny

どちらも目的が異なり、コンパイラーによって同じように扱われません。メソッドが値を返す必要がある場合、returnを使用する必要があります。メソッドは、複数の値を返す必要がある場合に使用されます。

Returnを使用する場合、データは最初にメソッドスタックに書き込まれ、次に呼び出し元のメソッドに書き込まれます。 outの場合、呼び出し元のメソッドスタックに直接書き込まれます。他に違いがあるかどうかはわかりません。

1
danish

実質的な違いはありません。outパラメーターはC#にあり、メソッドが1つ以上の値、つまりすべてを返すことができます。

ただし、若干の違いがありますが、それらのどれも非常に重要ではありません。

Outパラメーターを使用すると、次のような2行の使用が強制されます。

int n;
GetValue(n);

戻り値を使用すると、1行で実行できます。

int n = GetValue();

もう1つの違い(値型についてのみ、C#が関数をインライン化しない場合にのみ正しい)は、OUTパラメーターを使用しているときに関数が戻るときに必ず戻り値を使用すると値のコピーが作成されるとは限らないことです。

1
user88637

戻り値の型boolでoutキーワードを使用すると、コードの肥大化を抑え、読みやすさを向上できる場合があります。 (主に、出力パラメータの追加情報がしばしば無視される場合。)例えば:

var result = DoThing();
if (result.Success)
{
    result = DoOtherThing()
    if (result.Success)
    {
        result = DoFinalThing()
        if (result.Success)
        {
            success = true;
        }
    }
}

対:

var result;
if (DoThing(out result))
{
    if (DoOtherThing(out result))
    {
        if (DoFinalThing(out result))
        {
            success = true;
        }
    }
}
1
Paul Smith

他の人が言ったように:戻り値ではなく、出力パラメータ。

本「Framework Design Guidelines」(第2版)をお勧めできますか?ページ184〜185には、パラメータを回避する理由が記載されています。本全体で、あらゆる種類の.NETコーディングの問題について正しい方向に導くことができます。

フレームワーク設計ガイドラインに関連しているのは、静的解析ツールFxCopの使用です。これは、Microsoftのサイトで無料でダウンロードできます。コンパイルされたコードでこれを実行し、それが何を言っているかを見てください。それが何百ものことについて文句を言うなら...パニックにならないでください!それぞれのケースについて何を言っているかを静かに注意深く見てください。急いで物事を修正しないでください。それがあなたに言っていることから学ぶ。あなたは習得への道に置かれます。

1
Andrew Webb

さらに、戻り値は非同期設計パラダイムと互換性があります。

refまたはoutパラメーターを使用する場合、関数「async」を指定できません。

要約すると、戻り値メソッドチェーン、より明確な構文(呼び出し側が追加の変数を宣言する必要性を排除することにより)を許可し、将来大幅な変更を必要とせずに非同期設計を許可します。

1
firetiger77

管理されていないメモリを操作する場合に役立ついくつかのシナリオの1つであり、「返された」値はそれ自体で破棄されることを期待するのではなく、手動で破棄する必要があることを明らかにしたい。

1
xian

メソッドで宣言したオブジェクトを返そうとする場合、outはより便利です。

public BookList Find(string key)
{
   BookList book; //BookList is a model class
   _books.TryGetValue(key, out book) //_books is a concurrent dictionary
                                     //TryGetValue gets an item with matching key and returns it into book.
   return book;
}
0
Taera Kwon

戻り値は、メソッドによって返される通常の値です。

outパラメータとして、well outとrefはC#の2つのキーワードであり、変数をreferenceとして渡すことができます。

refoutの大きな違いは、refは前に初期化する必要があり、outはしない

0
user2983359