値型と参照型、ヒープ メモリとスタック メモリ、シャロー コピーとディープ コピーの C# 分析

要約: 値型と参照型の概念、分割、区別、およびそれらで使用されるさまざまな格納方法 (ヒープ メモリとスタック メモリ) について説明します。

プログラミング言語: C#

プログラミング環境:Visual Studio 2019

目次

値型と参照型の概念

共通の値の型と参照の型

値型と参照型の違いと関係

違い

接続

ヒープメモリとスタックメモリ

コンセプト

違い

浅いコピーと深いコピー

コンセプト

実装

まとめ 

それぞれのメッセージ


値型と参照型の概念

        C# では、型は値型と参照型の 2 つの型に分類されます。値型変数は、データを直接格納する変数を指します。そのインスタンスは通常、スタック上に割り当てられます (静的割り当て)。一方、参照型変数はデータ参照を保持します。データはデータ ヒープに格納され、そのインスタンスはヒープ上に割り当てられます (動的に割り当てられます)。

        概念だけを見ると抽象的かもしれないので、例を使って説明しましょう。

int i = 10;//定义值类型——int型
int[] ii = { 1, 2, 3 };//定义引用类型——数组

//分别输出这两个变量
Console.WriteLine(i);
Console.WriteLine(ii);

        プログラムを実行して出力する

        値型の場合は値が直接出力されるのに対し、参照型の場合は参照のみが出力されることがわかります。図に示すように、値型はデータが直接スタックに格納されるのに対し、参照型は参照のみがスタックに格納され、そのデータはヒープに格納されるためです。

 

共通の値の型と参照の型

共通の値の型と参照の型
値の型 基本的なデータ型 int、float、double、char、bool など
列挙型 列挙型
構造 構造体
参照型 親切 基本クラス オブジェクト、文字列、カスタム クラス class
インターフェース インターフェース
配列 さまざまなタイプの配列

   

値型と参照型の違いと関係

違い

  • メモリ内の保存方法は異なります。詳細は上で紹介しました。
  • メモリの回復方法が異なり、値型はスタックに格納され、使用後すぐにリサイクルされますが、参照型のデータはヒープに格納され、使用後はガベージ コレクションを待ちます(GC ガベージ コレクターが自動的にリサイクルします)。
  • 値型には宣言時に次のデータが含まれますが、参照型は宣言時にスタック上のポインターのみを定義し、ヒープ内のメモリ領域を開く前に new でインスタンス化する必要があります。インスタンス化されていない参照型の変数を使用した場合、対応するメモリ空間がないため、ヌルポインタ例外が発生します。例は次のとおりです。

  • 値型の割り当ての場合、同じ値を持つ新しいオブジェクトをコピーすることと同等ですが、参照型の割り当ての場合、元のオブジェクトへの参照と同等であり、その値を変更すると元のオブジェクトに影響します。(この処理は浅いコピーに属します) コード例は次のとおりです。

int i = 10;
int j = i;//i赋值给j,相当于复制一个同值新对象
j = 15;
Students student1 = new Students();//实例化学生1 张三 80
student1.Name = "张三";
student1.Score = 80;
Students student2 = student1;//学生1赋值给学生2,相当于student2拥有student1的引用
student2.Name = "李四"; 
student2.Score = 90;

//分别输出值类型i,j,发现i和j互不影响
Console.WriteLine(i);
Console.WriteLine(j);

//分别输出两个学生的信息,发现student1的信息被覆盖为student2
Console.WriteLine(student1.Name);
Console.WriteLine(student1.Score);
Console.WriteLine(student2.Name);
Console.WriteLine(student2.Score);

        プログラムを実行すると、出力は次のようになります

        値型と参照型では代入の意味が異なり、値型は新しいオブジェクトをコピーすること、参照型は参照を元のオブジェクトにコピーすることであることがわかります。

  • 関数のパラメータや戻り値として使用する場合もルールは上記と同様で、値の型は変数の入力パラメータまたは戻り値のコピー、参照型は変数のコピーの入力パラメータまたは戻り値への参照となります。価値。 
  • 参照型は新しい型を導出し、ポリモーフィズムをサポートできますが、値型はシールされており、ポリモーフィズムをサポートしません。

接続

  • 参照型はインターフェイスを実装でき、値型は構造体を備えたインターフェイスを実装できます。
  • 参照型は System.Object クラスから直接継承し、値型は Syetem.Object のサブクラスである System.ValueTpye クラスから直接継承します。

ヒープメモリとスタックメモリ

コンセプト

        C# プログラムが共通言語ランタイム (CLR) で実行される場合、メモリは論理的にスタックとヒープの 2 つのブロックに分割されます。ヒープはマネージド ヒープとも呼ばれ、スタックはスタックとも呼ばれます。

違い

  • スタック領域は後入れ先出しの原則に従っており、スタックの最上位要素が使い果たされるとすぐに解放されます。ヒープ領域は倉庫のようなもので、必要に応じて自動的に検索され呼び出されます。 GC でクリーニングする必要があります。
  • スタック領域は比較的小さいですが、データの読み取りは高速です。ヒープ領域は比較的大きいですが、データの読み取りは遅くなります。

浅いコピーと深いコピー

コンセプト

        オブジェクトの浅いコピーを作成する場合、それが値型メンバーの場合は、それ自体と同等の新しいオブジェクトがコピーされ、参照型メンバーの場合は、その参照のみがコピーされます。オブジェクトが深くコピーされると、参照型のメンバーが指すオブジェクトもコピーされます。元のオブジェクトとは関係のない参照型オブジェクトを完全にコピーしたいが、両者が完全に分離されている場合は、ディープコピーを使用する必要があります。

実装

        浅いコピーの場合、オブジェクトを新しいオブジェクトに直接割り当てるか、オブジェクトの MemberwiseClone() メソッドを使用して浅いコピーを実装します。ディープ コピーの場合、Xiaobian はより便利なメソッド、つまりディープ コピーを実現する逆シリアル化メソッドを導入しています。コードは次のとおりです: (注: このメソッドを呼び出す T は、シリアル化できることを示すために先頭に [Serializable] を追加する必要があります)

//反序列化深拷贝方法,T表示拷贝对象类,调用此方法需在T前加[Serializable]
public static T DeepCopyByBinary<T>(T obj)
{
    object retval;
    using (MemoryStream ms = new MemoryStream())
    {
        BinaryFormatter bf = new BinaryFormatter();
        bf.Serialize(ms, obj);
        ms.Seek(0, SeekOrigin.Begin);
        retval = bf.Deserialize(ms);
        ms.Close();
    }
    return (T)retval;
}

        コードに直接移動します。ここで、student2 は Student1 の浅いコピー、student3 は Student1 の深いコピーです。

Students student1 = new Students();//实例化学生1 张三 80
student1.Name = "张三";
student1.Score = 80;
Students student2 = student1;//学生1赋值给学生2,相当于浅拷贝
student2.Name = "李四"; 
student2.Score = 90;
Students student3 = DeepCopyByBinary(student1);//对学生1深拷贝,赋值给学生3
student3.Name = "王麻子";
student3.Score = 100;

//分别输出三个学生的信息
Console.WriteLine(student1.Name);
Console.WriteLine(student1.Score);
Console.WriteLine(student2.Name);
Console.WriteLine(student2.Score);
Console.WriteLine(student3.Name);
Console.WriteLine(student3.Score);

        出力は次のとおりです。

        浅いコピーの後、student2 はstudent1 への参照にすぎず、深いコピーの後、student3 とstudent1 は 2 つの独立したオブジェクトであることがわかります。

まとめ 

        C# プログラミングでは値の型と参照型を理解することが非常に重要であり、多くのバグの発生を避けることができます。コピーとディープ コピーにより、コピーのニーズに応じて適切なコピー方法を正しく選択できます。 。

それぞれのメッセージ

        真剣な姿勢が物事を動かし、より良いものにしていく、これが私たちが成長する上で捨てられない秘訣です。

おすすめ

転載: blog.csdn.net/lucgh/article/details/130362522