.NET の inout (C# の ref、vb.net の byref (out パラメータのような)) パラメータの適切な使用法はありますか?
入力と戻り値の両方に使用されるパラメーターによる混乱は、out パラメーターのパラメーターの数の増加、または配列を返すか、カスタム クラスを返すことよりも悪いと感じています。
9 に答える
私が遭遇した最も一般的な使用法(まだそれほど一般的ではありませんが、IMO)は、一種の「既存のオブジェクトを変更するか、必要に応じて作成する」ことです。例えば:
public void AppendToBuilder(ref StringBuilder builder)
{
if (builder == null)
{
builder = new StringBuilder();
}
builder.Append(/* whatever */);
}
StringBuilder
おそらく良い例ではありませんが、次のことを行う必要がない場合でも、オブジェクトの作成を回避できる場合があります。
public static string DoSomething(IEnumerable<Foo> foos)
{
// For an empty collection or where there aren't any
// frobulating foos, we don't need to create a builder
StringBuilder builder = null;
foreach (Foo foo in foos)
{
if (foo.Frobulates)
{
foo.AppendToBuilder(ref builder);
}
}
return builder == null ? null : builder.ToString();
}
私は主にレガシーコードに適応するためにそれを使用しました.= (com interop)。
また、パフォーマンスが必要で成功を示す必要があるコードで使用する傾向があります。
bool PerformSomeReadOperation(SomeInput obj, ref int defaultedOutput) { }
ここで、戻り値は成功または失敗の概念、あるいはおそらくエラー コードであり、defaultedOutput はデフォルト値とともに入ってくる値です。
(少なくとも CLR に関する限り) out と ref の間に実際の違いはないことをご存知ですか?
値を変更したいときにラッパークラスがやり過ぎだと思うときに使用します。
例えば
if (line[index].StartsWith("X12"))
ParseX12(lines, ref index, builder); //eats 2 or 4 lines
else if (line[index].StartsWith("A27"))
ParseA27(lines, ref index, builder); //eats 1 line
else if (line[index].StartsWith("B16"))
ParseB16(lines, ref index, builder); //eats 1 to 3 lines
else
index++; //this line couldn't be parsed, skip to the next one
この例では、解析関数が複数の行を消費する場合があります。したがって、それらはインデックス変数を正しく更新する責任があります。
参照および出力パラメーターの受け渡しモードは、メソッドがメソッド呼び出し元から渡された変数を変更できるようにするために使用されます。
各パラメーター受け渡しモード(refおよびout)は、さまざまなプログラミングニーズに適合するように設計されています。
outパラメーターを受け取るメソッドの呼び出し元は、呼び出しの前にoutパラメーターとして渡された変数に割り当てる必要はありません。ただし、メソッドは、戻る前にoutパラメータに割り当てる必要があります。
パラメータを考える1つの方法は、それらがメソッドの追加の戻り値のようなものであるということです。これらは、メソッドが複数の値を返す必要がある場合に便利です。
参照渡しの概念と参照型の概念を混同しないでください。
2つの概念は関連していません。メソッドパラメータは、値型であるか参照型であるかに関係なく、refによって変更できます。参照によって渡される場合、値型のボックス化はありません。
垂直レイアウトでGDIウィンドウにテキストを印刷するグラフィックルーチンの値型パラメーターで使用しました。inoutパラメータは、現在のY位置を追跡します。
WriteString("hello", ref y);
それよりも
y += WriteString("hello", y);
Outは、複数の戻り値を必要とする単純なケースに適しています。このようにして、「それはパラメータであるか/それは戻り値であるか」によって混乱することはありませんref
。
public void GetStockQuote(
string stock,
out double lastTrade,
out DateTime tradeTime,
out double change,
out double open)
{
//perform stock magic here
}
あなたは本当にあなた自身の質問に答えました。
パラメータを介してデータを出し入れすることが理にかなっている場合 - つまり。メソッドが現在の値を知る必要があり、それを更新する (または、参照型の場合はそれを置き換える) ことが予想される場合は、ref が正しいです。頻繁に発生するわけではありませんが、発生した場合は、何を使用すればよいかがわかります ;-)
同じ変数を変更する一連の呼び出しを行う可能性が高い場合。
It doesn't happen much in pointer-based languages like C# though, because you can just pass an object as an 'in' parameter and the called function can call its methods to modify it as necessary.
ごくまれに、主にパフォーマンス上の理由で役立つ場合があります。
ほとんどの場合、あなたが言及したように、配列またはカスタムクラスを返すことで回避できます。私の意見では回避する必要があります。