共通型システムの概要
更新 : 2007 年 11 月
ここでは、使用言語で共通型システム (CTS: Common Type System) を実装する方法について理解し、実践するために役立つ概念、および関連用語の定義について説明します。
型の分類
共通型システムは、大きく分けて次の 2 つのカテゴリの型をサポートしています。それぞれの型は、さらにいくつかのサブカテゴリに分類されます。
値型
値型は、データを直接格納します。値型のインスタンスは、スタック上に割り当てられるか、構造体にインラインで割り当てられます。値型には、組み込みの (ランタイムによって実装される) 値型、ユーザー定義の値型、または列挙型があります。組み込みの値型の一覧については、「.NET Framework クラス ライブラリの概要」を参照してください。
参照型
参照型は、値のメモリ アドレスへの参照を格納する型であり、ヒープ上に割り当てられます。参照型には、自己記述型、ポインタ型、またはインターフェイス型があります。参照型の型は、自己記述型の場合は、値を見れば判断できます。自己記述型は、さらに配列型とクラス型に分けられます。クラス型は、ユーザー定義のクラス、ボックス化された値型、およびデリゲートです。
値型の変数は、それぞれ独自にデータのコピーを保持するため、ある変数に対する演算によって他の変数に影響が及ぶことはありません。参照型の場合は、複数の変数が同じオブジェクトを参照できるため、ある変数に対する演算によって、他の変数からも参照されているオブジェクトが影響を受ける可能性があります。
すべての型は System.Object 基本型から派生しています。
参照型と値型の違いを次の例に示します。
Class Class1
Public Value As Integer = 0
End Class 'Class1
Class Test
Shared Sub Main()
Dim val1 As Integer = 0
Dim val2 As Integer = val1
val2 = 123
Dim ref1 As New Class1()
Dim ref2 As Class1 = ref1
ref2.Value = 123
Console.WriteLine("Values: {0}, {1}", val1, val2)
Console.WriteLine("Refs: {0}, {1}", ref1.Value, ref2.Value)
End Sub 'Main
End Class 'Test
using System;
class Class1
{
public int Value = 0;
}
class Test
{
static void Main() {
int val1 = 0;
int val2 = val1;
val2 = 123;
Class1 ref1 = new Class1();
Class1 ref2 = ref1;
ref2.Value = 123;
Console.WriteLine("Values: {0}, {1}", val1, val2);
Console.WriteLine("Refs: {0}, {1}", ref1.Value, ref2.Value);
}
}
このプログラムによって、次のような出力が生成されます。
Values: 0, 123
Refs: 123, 123
これらのさまざまな型の関係を次の図に示します。型のインスタンスは、それらの型にサブカテゴリがある場合でも、単純な値型または自己記述型にすることができます。
型の分類
各型の詳細については、「値型」、「列挙型」、「クラス」、「デリゲート」、「配列」、「インターフェイス」、および「ポインタ」をそれぞれ参照してください。
値とオブジェクト
値はデータのバイナリ形式であり、型はこのデータを解釈する方法を提供するものです。値型は、その型のデータのバイナリ表現を直接格納しています。参照型の値は、その型のデータを表すビット シーケンスの位置を示しています。
すべての値には、その値の表現を定義する厳密な型と、その値に対して定義された操作とが関連付けられています。自己記述型の値をオブジェクトと呼びます。オブジェクトの厳密な型はそのオブジェクトの値を調べればわかりますが、値型またはポインタ型の場合は、値を調べても型の判断はつきません。1 つの値に、複数の型が関連付けられる場合もあります。インターフェイスを実装している型の値は、そのインターフェイス型の値にもなります。同様に、基本型から派生した型の値は、その基本型の値にもなります。
型とアセンブリ
ランタイムは、アセンブリを使用して型の検索および読み込みを行います。アセンブリ マニフェストは、アセンブリのスコープ内で記述されたすべての型参照を解決するためにランタイムが使用する情報を格納しています。
ランタイムにおける型名は、アセンブリ名と、そのアセンブリ内での型名という 2 つの論理的な部分から構成されます。同じ型名の型が 2 つある場合でも、それらの型が異なるアセンブリに含まれているときは、それらは 2 つの異なる型として定義されます。
アセンブリによって、開発者が認識している名前のスコープと、ランタイム システムにおける名前のスコープとの一貫性が維持されます。開発者は、アセンブリのコンテキスト内で型を作成します。開発者がビルドするアセンブリの内容に従って、実行時に使用される名前のスコープが決定されます。
型と名前空間
ランタイムの視点では、名前空間は型名のコレクションに過ぎません。言語によっては、開発者が型の論理グループを形成するために使用できる構成体とそれに対応した構文がある場合もありますが、それらの構成体はランタイムが型をバインドするときには使用されません。つまり、Object クラスと String クラスはいずれも System 名前空間に含まれていますが、ランタイムが認識するのは、それぞれの型の完全名 (System.Object と System.String) だけです。
そのため、2 つの異なる名前空間階層に含まれる 2 つの型 (たとえば System.Collections と System.Windows.Forms) をまとめて公開する 1 つのアセンブリをビルドできます。また、名前に MyDll.MyClass が含まれる型をエクスポートするアセンブリを 2 つビルドすることもできます。
アセンブリ内の型を名前空間階層に属する型として表すツールを作成する場合、そのツールでは、アセンブリまたはアセンブリ グループに含まれる型を列挙し、それらの型の名前を解析して階層関係を導き出す必要があります。