refキーワードを使用する主な目的は、変数の値が渡される関数によって変更できることを示すことです。変数を値で渡す場合、関数内からの更新は元のコピーに影響しません。
複数の戻り値が必要で、戻り値用の特別な構造体またはクラスを構築するのはやり過ぎになる状況で非常に便利です(そしてより高速です)。例えば、
public void Quaternion.GetRollPitchYaw(ref double roll, ref double pitch, ref double yaw){
roll = something;
pitch = something;
yaw = something;
}
これは、ポインターの使用が制限されていない言語のかなり基本的なパターンです。c / c ++では、クラスと配列をポインターとして、プリミティブが値によって渡されることがよくあります。C#は正反対のことをするので、上記のような状況では「ref」が便利です。
更新する変数をrefによって関数に渡す場合、結果を得るのに必要な書き込み操作は1回だけです。ただし、値を返すときは、通常、関数内の変数に書き込み、それを返し、宛先変数に再度書き込みます。データによっては、これにより不要なオーバーヘッドが追加される可能性があります。とにかく、これらはrefキーワードを使用する前に私が通常考慮する主なことです。
refは、c#でこのように使用すると少し速くなることがありますが、パフォーマンスの正当化として使用するには不十分です。
これは、7年前のマシンで、以下のコードを使用して、参照と値で100kの文字列を渡して更新したものです。
出力:
反復:10000000 byref:165ms byval:417ms
private void m_btnTest_Click(object sender, EventArgs e) {
Stopwatch sw = new Stopwatch();
string s = "";
string value = new string ('x', 100000); // 100k string
int iterations = 10000000;
//-----------------------------------------------------
// Update by ref
//-----------------------------------------------------
sw.Start();
for (var n = 0; n < iterations; n++) {
SetStringValue(ref s, ref value);
}
sw.Stop();
long proc1 = sw.ElapsedMilliseconds;
sw.Reset();
//-----------------------------------------------------
// Update by value
//-----------------------------------------------------
sw.Start();
for (var n = 0; n < iterations; n++) {
s = SetStringValue(s, value);
}
sw.Stop();
long proc2 = sw.ElapsedMilliseconds;
//-----------------------------------------------------
Console.WriteLine("iterations: {0} \nbyref: {1}ms \nbyval: {2}ms", iterations, proc1, proc2);
}
public string SetStringValue(string input, string value) {
input = value;
return input;
}
public void SetStringValue(ref string input, ref string value) {
input = value;
}