.NET の構造体とクラスの違いは何ですか?
-
08-06-2019 - |
質問
.NET の構造体とクラスの違いは何ですか?
解決
.NET では、型には 2 つのカテゴリがあります。 参照型 そして 値の型.
構造体は 値の型 そしてクラスは 参照型.
一般的な違いは、 参照型 ヒープ上に存在し、 値の型 インラインで存在します。つまり、変数またはフィールドが定義されている場所に存在します。
を含む変数 値の型 全体が含まれています 値の型 価値。構造体の場合、変数には構造体全体とそのすべてのフィールドが含まれることを意味します。
を含む変数 参照型 ポインターが含まれているか、 参照 実際の値が存在するメモリ内の別の場所にコピーされます。
まず、これには 1 つの利点があります。
- 値の型 常に値が含まれています
- 参照型 を含めることができます ヌル-reference、現時点では何も参照していないことを意味します
内部的に、 参照型はポインターとして実装されており、それを知っていて、変数の代入がどのように機能するかを知っていると、他の動作パターンもあります。
- の内容をコピーする 値の型 変数を別の変数にコピーすると、内容全体が新しい変数にコピーされ、2 つは区別されます。言い換えれば、コピー後、一方を変更してももう一方には影響しません。
- の内容をコピーする 参照型 変数を別の変数にコピーすると、参照がコピーされます。つまり、同じ変数への参照が 2 つあることになります。 どこか別の場所 実際のデータの保存。言い換えれば、コピー後、一方の参照のデータを変更すると、もう一方の参照にも影響があるように見えますが、これは実際には両方の場所で同じデータを見ているだけであるためです。
変数またはフィールドを宣言する場合、2 つの型の違いは次のとおりです。
- 変数: 値の型 スタック上に存在し、 参照型 実際のメモリが存在するヒープメモリ内のどこかへのポインタとしてスタック上に存在します(ただし、 エリック・リッパーツの記事シリーズ:スタックは実装の詳細です.)
- クラス/構造体フィールド: 値の型 完全に型の内部に存在し、 参照型 実際のメモリが存在するヒープ メモリ内のどこかへのポインタとして型内に存在します。
他のヒント
それぞれの簡単な要約:
クラスのみ:
- 継承をサポートできる
- 参照 (ポインタ) 型です
- 参照は null にすることもできます
- 新しいインスタンスごとにメモリのオーバーヘッドがある
構造体のみ:
- 継承をサポートできない
- 値の型です
- (整数など) 値によって渡されます
- Null 参照は使用できません (Nullable が使用されていない場合)
- 「ボックス化」されていない限り、新しいインスタンスごとにメモリのオーバーヘッドが発生しません。
クラスと構造体の両方:
- 複合データ型は通常、何らかの論理関係を持ついくつかの変数を含めるために使用されます。
- メソッドとイベントを含めることができます
- インターフェイスをサポートできる
.NET では、構造体宣言とクラス宣言により、参照型と値型が区別されます。
参照型を渡す場合、実際に保存されるのは 1 つだけです。インスタンスにアクセスするすべてのコードは、同じインスタンスにアクセスします。
値型を渡すと、それぞれがコピーになります。すべてのコードは独自のコピーで動作します。
これは次の例で示すことができます。
struct MyStruct
{
string MyProperty { get; set; }
}
void ChangeMyStruct(MyStruct input)
{
input.MyProperty = "new value";
}
...
// Create value type
MyStruct testStruct = new MyStruct { MyProperty = "initial value" };
ChangeMyStruct(testStruct);
// Value of testStruct.MyProperty is still "initial value"
// - the method changed a new copy of the structure.
クラスの場合は異なります
class MyClass
{
string MyProperty { get; set; }
}
void ChangeMyClass(MyClass input)
{
input.MyProperty = "new value";
}
...
// Create reference type
MyClass testClass = new MyClass { MyProperty = "initial value" };
ChangeMyClass(testClass);
// Value of testClass.MyProperty is now "new value"
// - the method changed the instance passed.
クラスには何も指定できません。参照は null を指すこともできます。
構造体は実際の値です。空にすることはできますが、null にすることはできません。このため、構造体には常にパラメーターのないデフォルトのコンストラクターがあり、「開始値」が必要です。
マイクロソフト社から クラスと構造体の選択 ...
経験則として、フレームワークのタイプの大部分はクラスでなければなりません。ただし、値タイプの特性により、構造体を使用する方が適切になる状況がいくつかあります。
✓ 構造体を検討する クラスの代わりに:
- その型のインスタンスが小さく、一般に寿命が短い場合、または一般に他のオブジェクトに埋め込まれている場合。
バツ 構造体を避ける タイプが持っていない限り、 全て 次の特性の:
- これは、プリミティブ型 (int、double など) と同様に、単一の値を論理的に表します。
- インスタンスのサイズは 16 バイト未満です。
- それは不変です。 (変えられない)
- 頻繁に箱詰めする必要はありません。
他の回答で説明されているすべての違いに加えて、次のとおりです。
- 構造体 明示的なパラメータのないコンストラクタを持つことはできません 一方、クラスでは次のことができます。
- 構造体 デストラクターを持つことはできません, 、一方、クラスは
- 構造体 継承できない 別の構造体またはクラスから継承できるのに対し、クラスは別のクラスから継承できます。(構造体とクラスの両方をインターフェイスから実装できます。)
すべての違いを説明するビデオをご覧になりたい場合は、チェックしてください。 パート 29 - C# チュートリアル - C# のクラスと構造体の違い.
クラスのインスタンスはマネージド ヒープに保存されます。インスタンスを「含む」変数はすべて、ヒープ上のインスタンスへの単なる参照です。オブジェクトをメソッドに渡すと、オブジェクト自体ではなく、参照のコピーが渡されます。
構造体 (技術的には値型) は、プリミティブ型と同様に、使用される場所に保存されます。コンテンツは、カスタマイズされたコピー コンストラクターを呼び出すことなく、いつでもランタイムによってコピーできます。値の型をメソッドに渡すには、カスタマイズ可能なコードを呼び出すことなく、値全体をコピーする必要があります。
C++/CLI 名によって区別がわかりやすくなります。「ref クラス」は最初に説明したクラス、「value クラス」は 2 番目に説明したクラスです。C# で使用されるキーワード「class」と「struct」は、単に学習する必要があるものです。
構造体とクラスの違い:
- 構造体は値型です 一方 クラスは参照型です.
- 構造体はスタックに保存されます 一方 クラスはヒープに保存されます.
- 値タイプは、それらが宣言されているメモリに値を保持しますが、参照タイプはオブジェクトメモリへの参照を保持します。
- 値の型はすぐに破棄されます スコープが失われた後、参照タイプはスコープが失われた後に変数を破壊するだけです。オブジェクトは後にゴミコレクターによって破壊されます。
- 構造体を別のstructにコピーすると、その構造体の新しいコピーが作成された1つのstructの修正が作成されません。
- クラスを別のクラスにコピーすると、参照変数のみをコピーします。
- 両方の参照変数はヒープ上の同じオブジェクトを指します。1 つの変数を変更すると、もう 1 つの参照変数に影響します。
- 構造体にはデストラクタを含めることはできません, ただし、クラスにはデストラクターを含めることができます。
- 構造体には明示的なパラメータのないコンストラクタを含めることはできません 一方、クラスは構造体では継承をサポートしませんが、クラスはそうします。どちらもインターフェイスからの継承をサポートします。
- 構造は密閉型です.
構造とクラス
構造体は値型なのでスタックに格納されますが、クラスは参照型なのでヒープに格納されます。
構造体は継承とポリモーフィズムをサポートしませんが、クラスは両方をサポートします。
デフォルトでは、すべての構造体のメンバーはパブリックですが、クラスのメンバーはデフォルトでは本質的にプライベートです。
構造体は値型であるため、構造体オブジェクトに null を割り当てることはできませんが、クラスの場合はそうではありません。
完成させるために、使用する場合には別の違いがあります。 Equals
このメソッドは、すべてのクラスと構造体によって継承されます。
クラスと構造体があるとします。
class A{
public int a, b;
}
struct B{
public int a, b;
}
Main メソッドには 4 つのオブジェクトがあります。
static void Main{
A c1 = new A(), c2 = new A();
c1.a = c1.b = c2.a = c2.b = 1;
B s1 = new B(), s2 = new B();
s1.a = s1.b = s2.a = s2.b = 1;
}
それから:
s1.Equals(s2) // true
s1.Equals(c1) // false
c1.Equals(c2) // false
c1 == c2 // false
それで, 、構造体は、点のような数値のようなオブジェクトに適しています (x 座標と y 座標を保存します)。そして、クラスは他の人に適しています。名前、身長、体重が同じでも、二人は二人です。
他の回答に加えて、注目に値する基本的な違いが 1 つあります。それは、メモリに保存される方法です。これはアレイのパフォーマンスに大きな影響を与える可能性があります。構造体は値型であるため、それらが指しているメモリ領域に値を格納します。クラスは参照型であるため、それらが指しているメモリ領域内のクラスを参照し、実際の値は別の場所に格納されます。
- 構造体を使用すると、データを格納するために、それを含むクラス内にメモリが割り当てられます。
- クラスの場合、それを含むクラスには、メモリの別の領域にある新しいクラスへのポインタが含まれるだけです。
これは配列にも当てはまります。したがって、構造体の配列はメモリ内では次のようになります。
[struct][struct][struct][struct][struct][struct][struct][struct]
クラスの配列は次のようになります
[pointer][pointer][pointer][pointer][pointer][pointer][pointer][pointer]
関心のある実際の値は、実際には配列には格納されず、メモリ内の別の場所に格納されます。
大部分のアプリケーションでは、この違いはあまり重要ではありませんが、高パフォーマンスのコードでは、これはメモリ内のデータの局所性に影響を与え、CPU キャッシュのパフォーマンスに大きな影響を与えます。構造体を使用できる/使用すべきときにクラスを使用すると、CPU でのキャッシュ ミスの数が大幅に増加します。
最近の CPU で最も遅いのは数値の処理ではなく、メモリからのデータのフェッチであり、L1 キャッシュ ヒットは RAM からのデータの読み取りよりも何倍も高速です。
まず、構造体は参照ではなく値によって渡されます。構造体は比較的単純なデータ構造に適していますが、クラスはポリモーフィズムと継承によりアーキテクチャの観点からより高い柔軟性を備えています。
おそらく他の人が私より詳しく教えてくれると思いますが、私は目的の構造が単純な場合に構造体を使用します。
アクセス指定子の基本的な違いと、上記のいくつかの違いに加えて、出力付きのコード サンプルを使用して、上記のいくつかの点を含む主要な違いのいくつかを追加したいと思います。これにより、参照と値をより明確に理解できるようになります。
構造体:
- 値型であり、ヒープ割り当てを必要としません。
- メモリ割り当てが異なり、スタックに格納されます
- 小規模なデータ構造に便利
- パフォーマンスに影響します。メソッドに値を渡すと、データ構造全体が渡され、すべてがスタックに渡されます。
- コンストラクターは単純に構造体の値自体を返し (通常はスタック上の一時的な場所にあります)、この値は必要に応じてコピーされます。
- 変数にはそれぞれデータの独自のコピーがあり、一方の操作が他方に影響を与えることはできません。
- ユーザー指定の継承はサポートされておらず、型オブジェクトから暗黙的に継承されます。
クラス:
- 参照タイプの値
- ヒープに保存される
- 動的に割り当てられたオブジェクトへの参照を保存する
- コンストラクターは new 演算子で呼び出されますが、ヒープにメモリは割り当てられません。
- 複数の変数が同じオブジェクトへの参照を持つ可能性があります
- 1 つの変数に対する操作が、他の変数によって参照されるオブジェクトに影響を与える可能性があります。
コードサンプル
static void Main(string[] args)
{
//Struct
myStruct objStruct = new myStruct();
objStruct.x = 10;
Console.WriteLine("Initial value of Struct Object is: " + objStruct.x);
Console.WriteLine();
methodStruct(objStruct);
Console.WriteLine();
Console.WriteLine("After Method call value of Struct Object is: " + objStruct.x);
Console.WriteLine();
//Class
myClass objClass = new myClass(10);
Console.WriteLine("Initial value of Class Object is: " + objClass.x);
Console.WriteLine();
methodClass(objClass);
Console.WriteLine();
Console.WriteLine("After Method call value of Class Object is: " + objClass.x);
Console.Read();
}
static void methodStruct(myStruct newStruct)
{
newStruct.x = 20;
Console.WriteLine("Inside Struct Method");
Console.WriteLine("Inside Method value of Struct Object is: " + newStruct.x);
}
static void methodClass(myClass newClass)
{
newClass.x = 20;
Console.WriteLine("Inside Class Method");
Console.WriteLine("Inside Method value of Class Object is: " + newClass.x);
}
public struct myStruct
{
public int x;
public myStruct(int xCons)
{
this.x = xCons;
}
}
public class myClass
{
public int x;
public myClass(int xCons)
{
this.x = xCons;
}
}
出力
構造体オブジェクトの初期値は次のとおりです。10
structオブジェクトの内部structメソッド内のメソッド値は次のとおりです。20
メソッド呼び出し後の Struct オブジェクトの値は次のとおりです。10
クラスオブジェクトの初期値は次のとおりです。10
クラスオブジェクトの内部クラスメソッド内のメソッド値は次のとおりです。20
メソッド呼び出し後のクラス オブジェクトの値は次のとおりです。20
ここでは、値による呼び出しと参照による呼び出しの違いが明確にわかります。
クラスで宣言されたイベントの += および -= アクセスは、lock(this) によって自動的にロックされ、スレッド セーフになります (静的イベントはクラスの型でロックされます)。構造体で宣言されたイベントの += および -= アクセスは自動的にロックされません。参照型式でのみロックできるため、構造体の lock(this) は機能しません。
構造体インスタンスを作成するとガベージ コレクションが発生することはありませんが (コンストラクターが参照型インスタンスを直接または間接的に作成しない限り)、参照型インスタンスを作成するとガベージ コレクションが発生する可能性があります。
構造体には常に、組み込みのパブリックなデフォルト コンストラクターがあります。
class DefaultConstructor { static void Eg() { Direct yes = new Direct(); // Always compiles OK InDirect maybe = new InDirect(); // Compiles if constructor exists and is accessible //... } }
これは、構造体は常にインスタンス化可能ですが、クラスはすべてのコンストラクターがプライベートである可能性があるため、インスタンス化できない可能性があることを意味します。
class NonInstantiable { private NonInstantiable() // OK { } } struct Direct { private Direct() // Compile-time error { } }
構造体にはデストラクターを含めることはできません。デストラクターは、object.Finalize をオーバーライドしたものにすぎません。構造体は値型であるため、ガベージ コレクションの対象になりません。
struct Direct { ~Direct() {} // Compile-time error } class InDirect { ~InDirect() {} // Compiles OK } And the CIL for ~Indirect() looks like this: .method family hidebysig virtual instance void Finalize() cil managed { // ... } // end of method Indirect::Finalize
構造体は暗黙的にシールされますが、クラスはシールされません。
構造体は抽象化できませんが、クラスは抽象化できます。
構造体は以下を呼び出すことができません:コンストラクター内でbase()を使用できますが、明示的な基本クラスを持たないクラスでは使用できます。
構造体は別のクラスを拡張できませんが、クラスは拡張できます。
構造体は、クラスが宣言できる保護されたメンバー (フィールド、ネストされた型など) を宣言できません。
構造体は抽象関数のメンバーを宣言できませんが、抽象クラスは宣言できます。
構造体は仮想関数のメンバーを宣言できませんが、クラスは宣言できます。
構造体はシールされた関数のメンバーを宣言できませんが、クラスは宣言できます。
構造体はオーバーライド関数のメンバーを宣言できませんが、クラスは宣言できます。
このルールの 1 つの例外は、構造体が System.Object、viz、Equals()、GetHashCode()、および ToString() の仮想メソッドをオーバーライドできることです。
前述したように:クラスは参照型であるのに対し、構造体はすべての結果を伴う値型です。
経験則として、フレームワーク設計ガイドラインでは、次の場合にはクラスの代わりに構造体を使用することを推奨しています。
- インスタンスのサイズが 16 バイト未満である
- プリミティブ型 (int、double など) と同様に、単一の値を論理的に表します。
- 不変です
- 頻繁に箱詰めする必要がなくなります
+-----------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
| | Struct | Class |
+-----------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
| Type | Value-type | Reference-type |
| Where | On stack / Inline in containing type | On Heap |
| Deallocation | Stack unwinds / containing type gets deallocated | Garbage Collected |
| Arrays | Inline, elements are the actual instances of the value type | Out of line, elements are just references to instances of the reference type residing on the heap |
| Aldel Cost | Cheap allocation-deallocation | Expensive allocation-deallocation |
| Memory usage | Boxed when cast to a reference type or one of the interfaces they implement, | No boxing-unboxing |
| | Unboxed when cast back to value type | |
| | (Negative impact because boxes are objects that are allocated on the heap and are garbage-collected) | |
| Assignments | Copy entire data | Copy the reference |
| Change to an instance | Does not affect any of its copies | Affect all references pointing to the instance |
| Mutability | Should be immutable | Mutable |
| Population | In some situations | Majority of types in a framework should be classes |
| Lifetime | Short-lived | Long-lived |
| Destructor | Cannot have | Can have |
| Inheritance | Only from an interface | Full support |
| Polymorphism | No | Yes |
| Sealed | Yes | When have sealed keyword |
| Constructor | Can not have explicit parameterless constructors | Any constructor |
| Null-assignments | When marked with nullable question mark | Yes (+ When marked with nullable question mark in C# 8+) |
| Abstract | No | When have abstract keyword |
| Access Modifiers | public, private, internal | public, protected, internal, protected internal, private protected |
+-----------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
構造体は実際の値です。空にすることはできますが、null にすることはできません。
これは事実ですが、.NET 2 では構造体が Nullable バージョンをサポートしており、C# は使いやすくするために糖衣構文を提供していることにも注意してください。
int? value = null;
value = 1;
「クラス vs 構造体」パズルの興味深いケースが 1 つあります。これは、メソッドから複数の結果を返す必要がある場合です。どれを使用するかを選択します。ValueTuple のストーリーを知っている場合は、ValueTuple (構造体) が追加されたのは、Tuple (クラス) よりも効果的であることがわかるはずです。しかし、それは数字で何を意味するのでしょうか?2 つのテスト:1 つは 2 つのフィールドを持つ構造体/クラスで、もう 1 つは 8 つのフィールドを持つ構造体/クラスです (ディメンションが 4 を超えると、クラスはプロセッサー ティックの点で構造体よりも効率的になるはずですが、もちろん GC 負荷も考慮する必要があります)。
追伸特定のケース「コレクションを含む構造体またはクラス」の別のベンチマークがあります。 https://stackoverflow.com/a/45276657/506147
BenchmarkDotNet=v0.10.10, OS=Windows 10 Redstone 2 [1703, Creators Update] (10.0.15063.726)
Processor=Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount=4
Frequency=3233540 Hz, Resolution=309.2586 ns, Timer=TSC
.NET Core SDK=2.0.3
[Host] : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT
Clr : .NET Framework 4.7 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.2115.0
Core : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT
Method | Job | Runtime | Mean | Error | StdDev | Min | Max | Median | Rank | Gen 0 | Allocated |
------------------ |----- |-------- |---------:|----------:|----------:|---------:|---------:|---------:|-----:|-------:|----------:|
TestStructReturn | Clr | Clr | 17.57 ns | 0.1960 ns | 0.1834 ns | 17.25 ns | 17.89 ns | 17.55 ns | 4 | 0.0127 | 40 B |
TestClassReturn | Clr | Clr | 21.93 ns | 0.4554 ns | 0.5244 ns | 21.17 ns | 23.26 ns | 21.86 ns | 5 | 0.0229 | 72 B |
TestStructReturn8 | Clr | Clr | 38.99 ns | 0.8302 ns | 1.4097 ns | 37.36 ns | 42.35 ns | 38.50 ns | 8 | 0.0127 | 40 B |
TestClassReturn8 | Clr | Clr | 23.69 ns | 0.5373 ns | 0.6987 ns | 22.70 ns | 25.24 ns | 23.37 ns | 6 | 0.0305 | 96 B |
TestStructReturn | Core | Core | 12.28 ns | 0.1882 ns | 0.1760 ns | 11.92 ns | 12.57 ns | 12.30 ns | 1 | 0.0127 | 40 B |
TestClassReturn | Core | Core | 15.33 ns | 0.4343 ns | 0.4063 ns | 14.83 ns | 16.44 ns | 15.31 ns | 2 | 0.0229 | 72 B |
TestStructReturn8 | Core | Core | 34.11 ns | 0.7089 ns | 1.4954 ns | 31.52 ns | 36.81 ns | 34.03 ns | 7 | 0.0127 | 40 B |
TestClassReturn8 | Core | Core | 17.04 ns | 0.2299 ns | 0.2150 ns | 16.68 ns | 17.41 ns | 16.98 ns | 3 | 0.0305 | 96 B |
コードテスト:
using System;
using System.Text;
using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes.Columns;
using BenchmarkDotNet.Attributes.Exporters;
using BenchmarkDotNet.Attributes.Jobs;
using DashboardCode.Routines.Json;
namespace Benchmark
{
//[Config(typeof(MyManualConfig))]
[RankColumn, MinColumn, MaxColumn, StdDevColumn, MedianColumn]
[ClrJob, CoreJob]
[HtmlExporter, MarkdownExporter]
[MemoryDiagnoser]
public class BenchmarkStructOrClass
{
static TestStruct testStruct = new TestStruct();
static TestClass testClass = new TestClass();
static TestStruct8 testStruct8 = new TestStruct8();
static TestClass8 testClass8 = new TestClass8();
[Benchmark]
public void TestStructReturn()
{
testStruct.TestMethod();
}
[Benchmark]
public void TestClassReturn()
{
testClass.TestMethod();
}
[Benchmark]
public void TestStructReturn8()
{
testStruct8.TestMethod();
}
[Benchmark]
public void TestClassReturn8()
{
testClass8.TestMethod();
}
public class TestStruct
{
public int Number = 5;
public struct StructType<T>
{
public T Instance;
public List<string> List;
}
public int TestMethod()
{
var s = Method1(1);
return s.Instance;
}
private StructType<int> Method1(int i)
{
return Method2(++i);
}
private StructType<int> Method2(int i)
{
return Method3(++i);
}
private StructType<int> Method3(int i)
{
return Method4(++i);
}
private StructType<int> Method4(int i)
{
var x = new StructType<int>();
x.List = new List<string>();
x.Instance = ++i;
return x;
}
}
public class TestClass
{
public int Number = 5;
public class ClassType<T>
{
public T Instance;
public List<string> List;
}
public int TestMethod()
{
var s = Method1(1);
return s.Instance;
}
private ClassType<int> Method1(int i)
{
return Method2(++i);
}
private ClassType<int> Method2(int i)
{
return Method3(++i);
}
private ClassType<int> Method3(int i)
{
return Method4(++i);
}
private ClassType<int> Method4(int i)
{
var x = new ClassType<int>();
x.List = new List<string>();
x.Instance = ++i;
return x;
}
}
public class TestStruct8
{
public int Number = 5;
public struct StructType<T>
{
public T Instance1;
public T Instance2;
public T Instance3;
public T Instance4;
public T Instance5;
public T Instance6;
public T Instance7;
public List<string> List;
}
public int TestMethod()
{
var s = Method1(1);
return s.Instance1;
}
private StructType<int> Method1(int i)
{
return Method2(++i);
}
private StructType<int> Method2(int i)
{
return Method3(++i);
}
private StructType<int> Method3(int i)
{
return Method4(++i);
}
private StructType<int> Method4(int i)
{
var x = new StructType<int>();
x.List = new List<string>();
x.Instance1 = ++i;
return x;
}
}
public class TestClass8
{
public int Number = 5;
public class ClassType<T>
{
public T Instance1;
public T Instance2;
public T Instance3;
public T Instance4;
public T Instance5;
public T Instance6;
public T Instance7;
public List<string> List;
}
public int TestMethod()
{
var s = Method1(1);
return s.Instance1;
}
private ClassType<int> Method1(int i)
{
return Method2(++i);
}
private ClassType<int> Method2(int i)
{
return Method3(++i);
}
private ClassType<int> Method3(int i)
{
return Method4(++i);
}
private ClassType<int> Method4(int i)
{
var x = new ClassType<int>();
x.List = new List<string>();
x.Instance1 = ++i;
return x;
}
}
}
}
プリミティブ値型または構造体型のすべての変数またはフィールドは、そのすべてのフィールド (パブリックおよびプライベート) を含む、その型の一意のインスタンスを保持します。対照的に、参照型の変数またはフィールドは null を保持したり、他の場所に格納されたオブジェクトを参照したり、他の参照がいくつでも存在する可能性があります。構造体のフィールドは、その構造体タイプの変数またはフィールドと同じ場所に格納されます。これはスタック上にある場合もあれば、スタック上にある場合もあります。 一部の 別のヒープ オブジェクト。
プリミティブ値タイプの変数またはフィールドを作成すると、デフォルト値を使用して作成されます。構造体タイプの変数またはフィールドを作成すると、新しいインスタンスが作成され、その中にデフォルトの方法ですべてのフィールドが作成されます。新しいものを作成する 実例 参照型の場合、まずデフォルトの方法ですべてのフィールドを作成し、次に型に応じてオプションの追加コードを実行します。
プリミティブ型の 1 つの変数またはフィールドを別の変数またはフィールドにコピーすると、値がコピーされます。構造体型の 1 つの変数またはフィールドを別の変数またはフィールドにコピーすると、前のインスタンスのすべてのフィールド (パブリックおよびプライベート) が後者のインスタンスにコピーされます。参照型の 1 つの変数またはフィールドを別の変数またはフィールドにコピーすると、後者は前者と同じインスタンス (存在する場合) を参照します。
C++ などの一部の言語では、型のセマンティックな動作はその格納方法とは無関係であることに注意することが重要ですが、.NET ではそうではありません。型が変更可能な値のセマンティクスを実装している場合、その型の 1 つの変数を別の変数にコピーすると、最初のインスタンスのプロパティが 2 番目のインスタンスによって参照される別のインスタンスにコピーされ、2 番目のメンバーを使用して変更すると、2 番目のインスタンスが変更されます。 、しかし最初ではありません。型が変更可能な参照セマンティクスを実装している場合、ある変数を別の変数にコピーし、2 番目の変数のメンバーを使用してオブジェクトを変更すると、最初の変数によって参照されるオブジェクトに影響します。不変セマンティクスを持つ型では変更が許可されないため、コピーによって新しいインスタンスが作成されるか、最初のインスタンスへの別の参照が作成されるかは、セマンティクス上は問題ではありません。
.NET では、すべてのフィールドが同様に実行できる場合、値型で上記のセマンティクスのいずれかを実装できます。ただし、参照型は可変参照セマンティクスまたは不変セマンティクスのみを実装できます。可変参照型のフィールドを持つ値型は、可変参照セマンティクスまたは奇妙なハイブリッド セマンティクスの実装に限定されます。