web-dev-qa-db-ja.com

C#値/オブジェクトはいつコピーされ、その参照はいつコピーされますか?

参照したいオブジェクトがコピーされたり、コピーしたいオブジェクトが参照されたりする場所で、同じ問題を何度も繰り返します。これは、=演算子を使用すると発生します。

たとえば、オブジェクトを別のフォームに送信する場合、つまり:

SomeForm myForm = new SomeForm();
SomeObject myObject = new SomeObject();
myForm.formObject = myObject;

...そしてフォーム内のオブジェクトを変更しても、元のオブジェクトは変更されません。オブジェクトがコピーされ、参照されていないかのようです。しかし、私がこれを行うと:

SomeObject myObject = new SomeObject();
SomeObject anotherObject = new SomeObject();
anotherObject = myObject;

...次にanotherObjectを変更すると、myObjectも変更されます。

最も深刻なケースは、定義済みのオブジェクトの1つを複製しようとした場合です。

public class SomeObject
{
    double value1, value2;

    //default constructor here

    public SomeObject(val1, val2)
    {
        value1 = val1;
        value2 = val2;
    }

    public void Clone(SomeObject thingToCopy)
    {
        this.value1 = thingToCopy.value1;
        this.value2 = thingToCopy.value2;
    }
}

私がこれをするとき...

SomeObject obj1 = new SomeObject(1, 2);
SomeObject obj2 = new SomeObject();
obj2.Clone(obj1);

...obj1が参照され、obj2への変更はobj1に変更されます。

上記のcloneメソッドの場合を除き、int, double, stringなどのシステムオブジェクトは常にコピーされるようです。

私の質問は、関数でのrefキーワードの使用を考慮せず、問題のすべてのケースでオブジェクトがコピーされ、オブジェクトが参照されるとき(つまり、関数に渡すとき、設定するとき)他のオブジェクトとして(上記の最初の2つの例のように)、3番目の例のようなメンバー変数をコピーするときなど)?

63
Mike Webb

この種の質問に丁寧に言葉を選ぶのにひどい時間をかけずに正確に答えることは難しい。

私はあなたが役に立つと思うかもしれないいくつかの記事でそうしました:

もちろん、記事が完璧であることを言うわけではありません-それとは程遠い-しかし、私はできる限り明確にしようとしました。

重要なことの1つは、頭の中で2つの概念(パラメーターの受け渡しと参照と値のタイプ)を分離することです。

特定の例を見るには:

SomeForm myForm = new SomeForm();
SomeObject myObject = new SomeObject();
myForm.formObject = myObject;

この意味は myForm.formObjectおよびmyObjectは、SomeObjectの同じインスタンスを参照します。2人が別々の紙片を持ち、それぞれに同じ住所が書かれているように。 1枚の紙の住所に行き、家を赤く塗ってから、2枚目の紙の住所に行くと、赤い家が見えます。

指定した型は不変であるため、「フォーム内のオブジェクトを変更する」ことの意味は明確ではありません。オブジェクト自体を変更する方法はありません。あなたになら変えられる myForm.formObjectSomeObjectの別のインスタンスを参照しますが、これは1枚の紙に住所を書き留めて、代わりに別の住所を書き込むようなものです。他の紙に書かれている内容は変わりません。

動作がわからない短いcompleteプログラムを提供できれば(理想的にはコンソールアプリケーション、短くてシンプルにするため)具体的な言葉で物事について話しやすくなります。

47
Jon Skeet

Hi Mike構造体やその他のプリミティブ型など、ValueTypeから派生するすべてのオブジェクトは値型です。つまり、変数に割り当てるか、メソッドのパラメーターとして渡すと必ずコピーされます。他のタイプは参照タイプです。つまり、参照タイプを変数に割り当てると、値ではなく、メモリ空間のアドレスが変数に割り当てられます。また、refキーワードを使用して値型を参照として渡すことができることに注意してください。構文は次のとおりです

public void MyMethod(ref int a) { a = 25 }
int i = 20;
MyMethod(ref i); //Now i get's updated to 25.

それが役に立てば幸い :)

7
Davita

オブジェクトのクローンに関して、あるオブジェクトから別のオブジェクトにコピーする値が参照型である場合、元のオブジェクトの値を変更すると、コピーされたオブジェクトの値に影響します(同じオブジェクトへの参照にすぎないため)

参照型のプロパティを持つオブジェクトを複製する必要がある場合は、それらの型を複製可能にするか、必要に応じて新しいインスタンスをインスタンス化して手動でコピーする必要があります。

IClonable インターフェイスを使用することを検討してください。ただし、これは最善のソリューションではありません。

1
RobV