このコードを考えてみましょう:
public class Program
{
private static void Main(string[] args)
{
var person1 = new Person { Name = "Test" };
Console.WriteLine(person1.Name);
Person person2 = person1;
person2.Name = "Shahrooz";
Console.WriteLine(person1.Name); //Output: Shahrooz
person2 = null;
Console.WriteLine(person1.Name); //Output: Shahrooz
}
}
public class Person
{
public string Name { get; set; }
}
明らかに、person1
をperson2
に割り当て、person2
のName
プロパティを変更すると、person1
のName
も変更されます。 person1
とperson2
は同じ参照を持っています。
person2 = null
の場合、person1
変数もnullにならないのはなぜですか?
person
とperson2
はどちらも同じオブジェクトへの参照です。しかし、これらは異なる参照です。だからあなたが走っているとき
person2 = null;
参照person2
のみを変更し、参照person
と対応するオブジェクトは変更しません。
これを説明する最良の方法は、簡略化した図を使用することです。これは、beforeのような状況でしたperson2 = null
:
そして、これがヌル代入後の画像です:
ご覧のとおり、2番目の画像ではperson2
は何も参照していません(厳密にはnull
を参照しています。参照なしとnull
への参照は異なる条件であるため、 Rune FS )、person
は既存のオブジェクトを参照しています。
_person1
_および_person2
_を、ストレージ内の特定の場所へのポインターと見なします。最初のステップでは、ストレージからのオブジェクトのアドレスを保持しているのは_person1
_のみであり、ストレージからのオブジェクトのメモリロケーションのアドレスを保持しているのは後で_person2
_です。後で_null
を_person2
_に割り当てても、_person1
_は影響を受けません。そのため、結果が表示されます。
あなたは読むことができます: ジョセフアルバハーリからの値対参照型
ただし、参照型の場合、オブジェクトはメモリ内に作成され、ポインタのようにではなく、別の参照を通じて処理されます。
次の図を使用して、同じ概念を描こうとします。
タイプpersonの新しいオブジェクトを作成し、_person1
_参照(ポインター)がストレージ内のメモリー位置を指しています。
ストレージ内で同じ参照を指す新しいreference(pointer)_person2
_を作成しました。
_person2
_を介してオブジェクトプロパティNameを新しい値に変更しました。両方の参照が同じオブジェクトを指しているため、Console.WriteLine(person1.Name);
はShahrooz
を出力します。
_person2
_参照にnull
を割り当てた後、それは何も指していませんが、_person1
_はまだオブジェクトへの参照を保持しています。
(最後に、メモリ管理では、 スタックは実装の詳細、パート1 および スタックは実装の詳細、パート2 から表示されます。リペルト)
person2
を参照null
に変更しましたが、person1
はそこで参照していません。
つまり、代入の前にperson2
とperson1
を見ると、どちらも同じオブジェクトを参照しています。次に、person2 = null
を割り当てます。これで、人物2は別のタイプを参照しています。 person2
が参照されているオブジェクトは削除されませんでした。
私はそれを説明するためにこのgifを作成しました:
参照をnull
に設定したからです。
null
への参照を設定すると、参照自体はnull
..です。参照するオブジェクトではありません。
それらを0からのオフセットを保持する変数と考えてください。person
の値は120です。person2
の値は120です。オフセット120のデータはPerson
オブジェクトです。これを行うと:
person2 = null;
..効果的に言っている、person2 = 0;
。ただし、person
の値は120のままです。
person
とperson2
の両方が同じオブジェクトを指す。したがって、どちらか一方の名前を変更すると、両方が変更されます(メモリ内の同じ構造を指すため)。
しかし、person2
をnull
に設定すると、person2
をnullポインターにして、同じpointにならないようにしますperson
としてのオブジェクトはもうありません。オブジェクト自体を破壊してオブジェクトを破壊することはなく、person
はまだオブジェクトをポイント/参照しているため、ガベージコレクションによって強制終了されることはありません。
person = null
も設定し、他にオブジェクトへの参照がない場合、最終的にはガベージコレクターによって削除されます。
参照型はオブジェクトIDを保持していると考えるのが最も役立ちます。クラス型Car
の変数がある場合、ステートメントmyCar = new Car();
はシステムに新しい車を作成してそのIDを報告するように要求します(オブジェクト#57だとしましょう)。次に、「オブジェクト#57」を変数myCar
に入れます。 Car2 = myCar;
、「オブジェクト#57」を変数Car2に書き込みます。 car2.Color = blue;
、それはシステムにCar2で識別された車(オブジェクト#57など)を見つけて青く塗るように指示します。
オブジェクトIDで直接実行される唯一の操作は、新しいオブジェクトの作成とIDの取得、「空白」のID(つまりnull)の取得、オブジェクトIDを保持できる変数または格納場所へのオブジェクトIDのコピー、オブジェクトIDが一致する(同じオブジェクトを参照する)。他のすべての要求は、IDによって参照されるオブジェクトを見つけて、そのオブジェクトに作用するようにシステムに要求します(IDまたはIDを保持する他のエンティティに影響を与えることなく)。
.NETの既存の実装では、オブジェクト変数はガベージコレクションされたヒープに格納されたオブジェクトへのポインターを保持する可能性がありますが、オブジェクト参照と他の種類のポインターの間に重大な違いがあるため、これは役に立たない実装の詳細です。ポインターは、通常、操作するのに十分な長さの間置かれるものの場所を表すと想定されています。オブジェクト参照にはありません。コードの一部は、アドレス0x12345678にあるオブジェクトへの参照を含むSIレジスターをロードし、それを使用し始め、ガベージコレクターがオブジェクトをアドレス0x23456789に移動している間に中断される場合があります。それは災害のように聞こえますが、ゴミはコードに関連付けられたメタデータを調べ、コードがSIを使用して使用していたオブジェクトのアドレス(つまり0x12345678)を保持していることを確認し、0x12345678にあったオブジェクトが移動されたことを確認します0x23456789に変更し、SIを更新して0x23456789が戻る前に保持します。そのシナリオでは、SIに格納されている数値はガベージコレクターによって変更されましたが、移動前と移動後は同じオブジェクトを参照していることに注意してください。移動前に、プログラムの起動以降に作成された23,592番目のオブジェクトを参照している場合は、その後も継続します。興味深いことに、.NETはほとんどのオブジェクトの一意で不変の識別子を格納しません。プログラムのメモリの2つのスナップショットが与えられた場合、最初のスナップショットの特定のオブジェクトが2番目のスナップショットに存在するかどうか、またはそのトレースがすべて破棄され、新しいオブジェクトが作成された場合、すべての観察可能な詳細。
person1
とperson2
は同じメモリアドレスを指します。 person2
をnullにすると、メモリアドレスではなく参照がnullになるため、person1
はそのメモリアドレスを参照し続けます。それが理由です。 Classs Person
をStruct
に変更すると、動作が変わります。
struct
に変更すると、値のセマンティクスを取得できることに注意してください。
public class Program
{
static void Main()
{
var person1 = new Person { Name = "Test" };
Console.WriteLine(person1.Name);
Person person2 = person1;
person2.Name = "Shahrooz";
Console.WriteLine(person1.Name);//Output:Test
Console.WriteLine(person2.Name);//Output:Shahrooz
person2 = new Person{Name = "Test2"};
Console.WriteLine(person2.Name);//Output:Test2
}
}
public struct Person
{
public string Name { get; set; }
}
参照タイプを作成すると、すべてのオブジェクトが同じメモリ位置を指すように実際に参照がコピーされます。ただし、Person2 = Nullを割り当てた場合、person2は参照人のコピーであり、次のようになるだけなので、効果はありません参照のコピーを消去。
person1とperson2は、ヒープ上の同じPersonオブジェクトを指す、スタック上の2つの個別の参照です。
参照の1つを削除すると、スタックから削除され、ヒープ上のPersonオブジェクトをポイントしなくなります。他の参照はそのまま残り、ヒープ上の既存のPersonオブジェクトを指しています。
Personオブジェクトへのすべての参照が削除されると、最終的にはガベージコレクターがオブジェクトをメモリから削除します。
まず、person1
への参照をperson2
にコピーします。これで、person1
とperson2
は同じオブジェクトを参照します。つまり、そのオブジェクトの値の変更(つまり、Name
プロパティの変更)は、両方の変数で確認できます。次に、nullを割り当てると、person2
に割り当てた参照が削除されます。現在person1
にのみ割り当てられています。参照自体がnotに変更されていることに注意してください。
参照によって引数を受け入れる関数がある場合、reference to person1
by referenceを渡すことができ、参照自体を変更できます。
public class Program
{
private static void Main(string[] args)
{
var person1 = new Person { Name = "Test" };
Console.WriteLine(person1.Name);
PersonNullifier.NullifyPerson(ref person1);
Console.WriteLine(person1); //Output: null
}
}
class PersonNullifier
{
public static void NullifyPerson( ref Person p ) {
p = null;
}
}
class Person {
public string Name{get;set;}
}
public class Program
{
private static void Main(string[] args)
{
var person = new Person {Name = "Test"};
Console.WriteLine(person.Name);
Person person2 = person;
person2.Name = "Shahrooz";
Console.WriteLine(person.Name);//Output:Shahrooz
// Here you are just erasing a copy to reference not the object created.
// Single memory allocation in case of reference type and parameter
// are passed as a copy of reference type .
person2 = null;
Console.WriteLine(person.Name);//Output:Shahrooz
}
}
public class Person
{
public string Name { get; set; }
}
言う:
_person2.Name = "Shahrooz";
_
_person2
_の参照に従い、参照がたまたまもたらすオブジェクトを "変更"(変更)します。参照自体(_person2
_の値)は変更されていません。同じ「アドレス」で同じインスタンスを参照しています。
次のように_person2
_に割り当てます:
_person2 = null;
_
referenceを変更します。オブジェクトは変更されません。この場合、参照矢印は1つのオブジェクトから「何もない」null
に「移動」します。しかし、person2 = new Person();
のような割り当ても、参照のみを変更します。オブジェクトは変更されません。