詳細:.Netの値と参照型

Microsoft .Netの型は、値型または参照型のいずれかです。値型は通常スタックに格納されますが、参照型はマネージヒープに格納されます。

値型はSystem.ValueTypeから派生し、独自のメモリ割り当て内にデータを含みます。言い換えると、変数、オブジェクト、または値型には、独自のデータのコピーがあります。

一方、参照型はSystem.Objectを拡張し、実際のデータを含むメモリ内の場所を指します。アクセスすると暗黙的に逆参照されるポインタに似た参照型を想像できます。 C#でサポートされている組み込みの参照型には、オブジェクト、文字列、動的などがあります。すべての基本的なデータ型、ブール値、日付、構造体、および列挙型は、値型の例です。参照型の例には、文字列、配列、クラスのオブジェクトなどがあります。C#で参照型を作成するには、class、interface、delegateのキーワードを利用できます。

参照型とは異なり、値型から派生したり、null値を値型に直接割り当てたりすることはできないことに注意してください。null許容型(.Net Frameworkの新しいバージョンに追加された機能)を利用することによってのみ、null値を値型に割り当てることができます。値型が別の型にコピーされると、値がコピーされます。したがって、それらの値を互いに独立して操作できます。一方を変更しても、もう一方には影響しません。逆に、参照型を別の型にコピーすると、その参照がコピーされます。それらの1つを変更すると、もう1つも影響を受けます。例として、参照の1つがnullに設定されている場合、もう1つもnullになります。

保管場所

CLRは、レジスタ、スタック、またはマネージヒープの3種類の格納場所にオブジェクトを格納します。有効期間の短いオブジェクトはレジスタまたはスタック内に格納されますが、有効期間の長いオブジェクトはヒープに格納されます。先に述べたように、値型は通常スタックに格納されます。

値型が常にスタックに格納されるというのはよくある誤解です。むしろ、変数が一時変数またはローカル変数であり、JITコンパイラーが値を登録しないことを決定した場合、値型スタックに格納できると言いたいです。本質的に、値型の実際の場所は、JITコンパイラーの実装に依存します。値型がオブジェクト内に含まれている場合、つまり参照型の一部である場合、値型はスタックフレーム、CPUレジスタ、またはヒープメモリに格納できることに注意してください。逆に、参照型はGCヒープに格納されます。オブジェクトがヒープに割り当てられている間、参照はスタックに格納されます。

値型のインスタンスまたは参照は、インスタンスまたは参照の存続期間が短命であるか長命であるかに応じて、スタック、レジスタ、またはヒープに格納されます。値型は、ローカル変数の場合はスタックに、クラスのフィールドの場合はマネージヒープに存在できます。つまり、参照型に属しているか、参照型の一部です。

値による受け渡しと参照による受け渡し

次のコードリストは、変数を値でメソッドに渡す方法を示しています。

 static void Increment(int i)

        {

            i = i + 1;

        }

        static void Main()

        {

            int x = 1;

            Increment(x);

            Console.WriteLine("The value of x is: " +x);

            Console.Read();

        }

refキーワードを使用すると、メソッドへの参照として値型を渡すことができることに注意してください。次のコードリストはこれを示しています。

static void Increment(ref int i)

        {

            i = i + 1;

        }

        static void Main()

        {

            int x = 1;

            Increment(ref x);

            Console.WriteLine("The value of x is: " +x);

            Console.Read();

        }

上記のコードを実行すると、コンソールに「xの値は2です」というメッセージが表示されます。

ボクシングと開封

値型から参照型への変換は、ボクシングと呼ばれます。ボックス化解除は正反対です。これは、参照型を値型に変換するプロセスとして定義されています。次のコードスニペットは、C#でのボックス化とボックス化解除を示しています。

int i = 100;

Object obj = i; //Boxing

i = (int) obj; //Unboxing