値型と参照型 (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;
}

参照

概念

C# 言語の概要

組み込みデータ型 (Visual C# Express)

配列とコレクション (Visual C# Express)

参照

値型 (C# リファレンス)

参照型 (C# リファレンス)

ボックス化とボックス化解除 (C# プログラミング ガイド)