値型と参照型 (Visual C# Express)
更新 : 2007 年 11 月
よく利用されている一部のプログラミング言語とは異なり、C# では "値" と " 参照" という 2 つのデータ型があります。開発するアプリケーションにとってパフォーマンスが重要である場合や、C# によるデータおよびメモリの管理方法に興味がある場合には、この相違を理解しておくことが大切です。
基本の組み込みデータ型またはユーザー定義の構造体を使用して変数が宣言されている場合、それは値型です。例外として string データ型があります。これは参照型です。
値型の場合、スタックで割り当てられているメモリに内容が格納されます。たとえば、次の例では、スタックと呼ばれるメモリ領域に値 42 が格納されます。
int x = 42;
定義されているメソッドの実行が終了したことに起因して変数 x が適用範囲を外れると、値はスタックから破棄されます。
スタックの使用は効率的ですが、値型の有効期間は限られているため、複数のクラス間におけるデータ共有には適していません。
これに対して、クラス インスタンスや配列などの参照型は、"ヒープ" と呼ばれるメモリ領域に割り当てられます。次に示す例では、配列を構成する 10 の整数に必要な空間がヒープに割り当てられています。
int[] numbers = new int[10];
メソッドが終了しても、このメモリ領域はヒープに返されません。このメモリ領域は、C# のガベージ コレクション システムが不要であると判断した時点でクリアされます。参照型の宣言ではオーバーヘッドが大きくなりますが、他のクラスからアクセスできるという利点があります。
ボックス化とボックス化解除
ボックス化とは、値型を参照型に変換する処理をいいます。変数をボックス化すると、ヒープ上の新しいコピーを指す参照変数が作成されます。この参照変数はオブジェクトであるため、すべてのオブジェクトが継承するすべてのメソッド (ToString() など) を使用できます。コードは次のようになります。
int i = 67; // i is a value type
object o = i; // i is boxed
System.Console.WriteLine(i.ToString()); // i is boxed
オブジェクトでの使用を目的としたクラスを使用する場合 (整数を格納するために ArrayList を使用する場合など) には、ボックス化解除が行われます。ArrayList に整数を格納すると、ボックス化されます。整数を取得するには、ボックス化を解除する必要があります。
System.Collections.ArrayList list =
new System.Collections.ArrayList(); // list is a reference type
int n = 67; // n is a value type
list.Add(n); // n is boxed
n = (int)list[0]; // list[0] is unboxed
パフォーマンスの問題
ここではパフォーマンスについて少し掘り下げて説明します。データが値型パラメータとしてメソッドに渡されると、スタックに各パラメータのコピーが作成されます。そのパラメータが大きいデータ型である場合 (たとえば、多数の要素から成るユーザー定義の構造体である場合)、またはメソッドが何度も実行される場合、パフォーマンスに影響を及ぼすことがあります。
このような状況では、ref キーワードを使用して参照を型に渡すことが適切な場合があります。この C# での手法は、変数を指すポインタを関数に渡す C++ での手法に相当します。C++ では、メソッドが変数の内容を変更できますが、常に安全であるとは限りません。プログラマは、セキュリティとパフォーマンスの兼ね合いに基づいて判断を下す必要があります。
int AddTen(int number) // parameter is passed by value
{
return number + 10;
}
void AddTen(ref int number) // parameter is passed by reference
{
number += 10;
}
out キーワードは ref キーワードに似ています。ただし、コンパイラに対し、メソッドがパラメータに値を割り当てる必要があることを通知します。値が割り当てられないと、コンパイル エラーが発生します。
void SetToTen(out int number)
{
// If this line is not present, the code will not compile.
number = 10;
}