オブジェクトを渡すときに「ref」キーワードを使用する理由
-
06-07-2019 - |
質問
オブジェクトをメソッドに渡す場合、なぜrefキーワードを使用する必要があるのですか?とにかくこれはデフォルトの動作ではありませんか?
例:
class Program
{
static void Main(string[] args)
{
TestRef t = new TestRef();
t.Something = "Foo";
DoSomething(t);
Console.WriteLine(t.Something);
}
static public void DoSomething(TestRef t)
{
t.Something = "Bar";
}
}
public class TestRef
{
public string Something { get; set; }
}
出力は<!> quot; Bar <!> quot;です。これは、オブジェクトが参照として渡されたことを意味します。
解決
オブジェクトの内容を変更する場合は、ref
を渡します:
TestRef t = new TestRef();
t.Something = "Foo";
DoSomething(ref t);
void DoSomething(ref TestRef t)
{
t = new TestRef();
t.Something = "Not just a changed t, but a completely different TestRef object";
}
DoSomethingを呼び出した後、t
は元のnew TestRef
を参照せず、完全に異なるオブジェクトを参照します。
これは、不変オブジェクトの値を変更する場合にも役立ちます。 string
。一度作成したint
の値は変更できません。しかし、<=>を使用することにより、異なる値を持つ別の文字列の文字列を変更する関数を作成できます。
編集:他の人が言ったように。必要でない限り、<=>を使用することはお勧めできません。 <=>を使用すると、メソッドが他の引数を自由に変更できるため、メソッドの呼び出し元をコーディングして、この可能性を確実に処理する必要があります。
また、パラメータタイプがオブジェクトの場合、オブジェクト変数は常にオブジェクトへの参照として機能します。これは、<=>キーワードを使用すると、参照への参照が得られることを意味します。これにより、上記の例で説明されているように実行できます。ただし、パラメータタイプがプリミティブ値(例:<=>)の場合、このパラメータがメソッド内で割り当てられている場合、渡された引数の値はメソッドが戻った後に変更されます:
int x = 1;
Change(ref x);
Debug.Assert(x == 5);
WillNotChange(x);
Debug.Assert(x == 5); // Note: x doesn't become 10
void Change(ref int x)
{
x = 5;
}
void WillNotChange(int x)
{
x = 10;
}
他のヒント
<!> quot;値で参照を渡す<!> quot;と、<!> quot;参照でパラメータ/引数を渡す<!> quot;を区別する必要があります。
このテーマに関するかなり長い記事を作成しました。これがニュースグループに登場する時間:)
.NETでは、メソッドにパラメーターを渡すと、コピーが作成されます。値の型では、値に加えた変更はすべてメソッドスコープで行われ、メソッドを終了すると失われます。
参照タイプを渡すと、コピーも作成されますが、これは参照のコピーです。つまり、同じオブジェクトへの2つの参照がメモリ内にあります。したがって、参照を使用してオブジェクトを変更すると、変更されます。ただし、参照自体を変更する場合(コピーであることを覚えておく必要があります)、メソッドを終了すると変更も失われます。
人々が以前に言ったように、割り当ては参照の修正であり、したがって失われます:
public void Method1(object obj) {
obj = new Object();
}
public void Method2(object obj) {
obj = _privateObject;
}
上記のメソッドは、元のオブジェクトを変更しません。
サンプルの少しの修正
using System;
class Program
{
static void Main(string[] args)
{
TestRef t = new TestRef();
t.Something = "Foo";
DoSomething(t);
Console.WriteLine(t.Something);
}
static public void DoSomething(TestRef t)
{
t = new TestRef();
t.Something = "Bar";
}
}
public class TestRef
{
private string s;
public string Something
{
get {return s;}
set { s = value; }
}
}
TestRefはクラス(参照オブジェクト)であるため、refとして渡すことなくt内の内容を変更できます。ただし、tをrefとして渡すと、TestRefは元のtが参照するものを変更できます。つまり、別のオブジェクトを指すようにします。
ref
を使用すると、次のように記述できます。
static public void DoSomething(ref TestRef t)
{
t = new TestRef();
}
そして、メソッドの完了後にtが変更されます。
object identifiers という形式の参照型(たとえばfoo
)の変数(たとえばList<T>
)を<!> quot; Object#24601 <!> quot;の形式で考える。ステートメントfoo = new List<int> {1,5,7,9};
により、foo.Length
が<!> quot; Object#24601 <!> quot;を保持するとします。 (4つのアイテムのリスト)。次に、ref
を呼び出すと、オブジェクト#24601の長さが要求され、4と応答するため、<=>は4になります。
<=>を<=>を使用せずにメソッドに渡すと、そのメソッドはオブジェクト#24601を変更する場合があります。このような変更の結果、<=>は4に等しくなくなる可能性があります。ただし、メソッド自体は<=>を変更できず、<!> quot; Object#24601 <!> quot;を保持し続けます。
<=>を<=>パラメーターとして渡すと、呼び出されたメソッドがオブジェクト#24601だけでなく、<=>自体にも変更を加えることができます。このメソッドは、新しいオブジェクト#8675309を作成し、その参照を<=>に保存します。その場合、<=>は<!> quot; Object#24601 <!> quot;ではなく、<!> quot; Object#8675309 <!> quot;を保持します。
実際には、参照型変数は<!> quot; Object#8675309 <!> quot ;;の形式の文字列を保持しません。意味のある数に変換できるものは何も保持していません。各参照型変数はビットパターンを保持しますが、そのような変数に格納されているビットパターンとそれらが識別するオブジェクトとの間に固定された関係はありません。コードがオブジェクトまたはその参照から情報を抽出し、コードが元のオブジェクトを識別する参照を保持または認識していない限り、別の参照が同じオブジェクトを識別するかどうかを後で判断する方法はありません。
これは、Cのポインターにポインターを渡すようなものです。おそらく設計上の問題があります!
参照タイプでref
キーワードを使用すると、参照への参照を効果的に渡すことができます。多くの点でout
キーワードを使用するのと同じですが、メソッドが<=> 'edパラメーターに実際に何かを割り当てる保証がないという小さな違いがあります。
ref
は、2つのスコープのグローバルエリアとして模倣(または動作)します。
- 発信者
- 呼び出し先。
ただし、値を渡す場合は、状況が異なります。参照によって値を強制的に渡すことができます。これにより、たとえば整数をメソッドに渡し、ユーザーに代わってメソッドに整数を変更させることができます。
Refは、関数がオブジェクト自体を操作できるか、値のみを操作できるかを示します。
参照渡しは言語にバインドされていません。これは、値渡し、名前渡し、必要性渡しなどの横にあるパラメータバインド戦略です。
補足:クラス名TestRef
は、このコンテキストではひどく悪い選択です;)。