2013-09-03T00:37:46の更新内容

programming/netfx/valuetype_referencetype/index.wiki.txt

current previous
1,1871 0,0
+
${smdncms:title,値型と参照型}
+
${smdncms:keywords,値型,参照型,value type,reference type,クラス,構造体,ボックス化}
+
${smdncms:document_versions,codelang=cs,codelang=vb}
+

          
+
.NET Frameworkの型システムでは、型は''値型''(value type)と''参照型''(reference type)の二種類に大別されます。 具体的には、int, float, char, boolなどの基本型・構造体・列挙体などが値型となります。 また、オブジェクト型(object)・文字列型(string)・クラス・インターフェイスなどが参照型となります。
+

          
+
値型はデータに直接アクセスする型で、参照型は参照によってデータの実体にアクセスする型です。 C#やVB.NETなど.NET Frameworkの型システムをベースとする言語でも、値型と参照型の分類が存在します。
+

          
+
-関連するページ
+
--[[programming/netfx/basic_types/0_characteristics]]
+
--[[programming/netfx/cloning]]
+
--[[programming/netfx/disposing]]
+
--[[programming/netfx/comparison/1_equation]]
+
--[[programming/netfx/arrays/3_structfields]]
+
--[[programming/netfx/tips/set_struct_field_using_reflection]]
+
--[[programming/vb.net/diff_7.xto8/06_constraints]] (VB)
+
--[[programming/vb.net/basics/09_structure]] (VB)
+
--[[programming/vb.net/basics/12_class]] (VB)
+

          
+
#googleadunit
+

          
+
*値型と参照型 [#abstract]
+
**値型と参照型の違い
+
値型と参照型の違いは値(インスタンス)へのアクセス方法にあります。 値型の変数は常になんらかの値(インスタンス)を格納していて、その値に対して直接アクセスします。 一方、参照型の変数はインスタンスへの''参照''を格納していて、参照を通して実体(インスタンス)にアクセスします。
+

          
+
また変数への代入時の動作も異なります。 値型の変数に対して代入を行う場合は代入元の''値がコピーされて代入される''のに対して、参照型の変数に対して代入を行う場合は''参照のみがコピーされて代入されます''。 この時、インスタンス自体はコピーされません。
+

          
+
このため、値型では変数がそれぞれ別の値(インスタンス)を持つことになるため同一のインスタンスを参照することはありませんが、参照型では変数に格納される参照しだいでは同一の実体(インスタンス)を参照することもあります。
+

          
+
次のコードではこの違いを明確にしています。 ここで、構造体は値型、クラスは参照型であることを念頭に置いてください。
+

          
+
#column
+
#tabpage(codelang=cs,container-title=値型の代入)
+
#code{{
+
using System;
+

          
+
// 構造体(値型)
+
struct ValType {
+
  public int ID;
+

          
+
  public ValType(int id)
+
  {
+
    ID = id;
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    ValType a = new ValType(1);
+

          
+
    // aをbに代入
+
    ValType b = a;
+
    // aの内容が複製されてbに代入されるため、
+
    // aとbは異なる実体を持つ
+

          
+
    Console.WriteLine("a.ID = {0}, b.ID = {1}",
+
                      a.ID, b.ID);
+

          
+
    // aに変更を加える
+
    a.ID = 2;
+
    // aとbは異なる実体であるため、aへの変更は
+
    // bには影響しない
+

          
+
    Console.WriteLine("a.ID = {0}, b.ID = {1}",
+
                      a.ID, b.ID);
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+

          
+
' 構造体(値型)
+
Structure ValType
+
  Public ID As Integer
+

          
+
  Public Sub New(ByVal id As Integer)
+
    Me.ID = id
+
  End Sub
+
End Structure
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim a As New ValType(1)
+

          
+
    ' aをbに代入
+
    Dim b As ValType = a
+
    ' aの内容が複製されてbに代入されるため、
+
    ' aとbは異なる実体を持つ
+

          
+
    Console.WriteLine("a.ID = {0}, b.ID = {1}", _
+
                      a.ID, b.ID)
+

          
+
    ' aに変更を加える
+
    a.ID = 2
+
    ' aとbは異なる実体であるため、aへの変更は
+
    ' bには影響しない
+

          
+
    Console.WriteLine("a.ID = {0}, b.ID = {1}", _
+
                      a.ID, b.ID)
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
a.ID = 1, b.ID = 1
+
a.ID = 2, b.ID = 1
+
}}
+

          
+
#column
+

          
+
#tabpage(codelang=cs,container-title=参照型の代入)
+
#code{{
+
using System;
+

          
+
// クラス(参照型)
+
class RefType {
+
  public int ID;
+

          
+
  public RefType(int id)
+
  {
+
    ID = id;
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    RefType a = new RefType(1);
+

          
+
    // aをbに代入
+
    RefType b = a;
+
    // (aのインスタンスへの参照がbに代入されるため、
+
    // aとbは同一の実体を参照する)
+

          
+
    Console.WriteLine("a.ID = {0}, b.ID = {1}",
+
                      a.ID, b.ID);
+

          
+
    // aに変更を加える
+
    a.ID = 2;
+
    // aとbは同一の実体を参照するため、aの実体への
+
    // 変更はbの実体に変更を加えるのと同じ事となる
+

          
+
    Console.WriteLine("a.ID = {0}, b.ID = {1}",
+
                      a.ID, b.ID);
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+

          
+
' クラス(参照型)
+
Class RefType
+
  Public ID As Integer
+

          
+
  Public Sub New(ByVal id As Integer)
+
    Me.ID = id
+
  End Sub
+
End Class
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim a As New RefType(1)
+

          
+
    ' aをbに代入
+
    Dim b As RefType = a
+
    ' (aのインスタンスへの参照がbに代入されるため、
+
    ' aとbは同一の実体を参照する)
+

          
+
    Console.WriteLine("a.ID = {0}, b.ID = {1}", _
+
                      a.ID, b.ID)
+

          
+
    ' aに変更を加える
+
    a.ID = 2
+
    ' aとbは同一の実体を参照するため、aの実体への
+
    ' 変更はbの実体に変更を加えるのと同じ事となる
+

          
+
    Console.WriteLine("a.ID = {0}, b.ID = {1}", _
+
                      a.ID, b.ID)
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
a.ID = 1, b.ID = 1
+
a.ID = 2, b.ID = 2
+
}}
+
#column-end
+

          
+
このように、代入を行った後に代入元(変数a)に変更を加えた場合、値型の場合では代入先(変数b)に影響しないのに対して、参照型の場合では見かけ上代入元への変更は代入先にも影響するような動作となります。
+

          
+
この他にも値型と参照型でいくつかの相違点があり、まとめると次のようになります。
+

          
+
|*値型と参照型の相違点
+
||~値型|~参照型|h
+
|~変数に代入される値|インスタンス(値)そのもの|インスタンスへの参照|
+
|~代入時の動作|値の複製(コピー)が代入される|参照が代入される|
+
|~ヌル参照|変数をヌル参照にすることはできない|変数をヌル参照にすることができる|
+
|~デフォルトコンストラクタ|暗黙的に実装される&br;明示的に実装することはできない|必要なら明示的に実装することができる|
+
|~インスタンスがアロケートされる場所|スタック|ヒープ|
+
|~インスタンスが破棄されるタイミング|スコープから脱した時点で破棄される|ガベージコレクタにより定期的に破棄される|
+

          
+

          
+
**値型と参照型の分類
+
.NET Frameworkの型システムにおいては、値型と参照型のどちらに分類されるかは明確に定められています。 値型・参照型となる型はそれぞれ次のようになります。
+

          
+
-''値型''
+
--[[プリミティブ型>programming/netfx/basic_types/0_characteristics#BasicTypes]]
+
---数値型 (int, float, IntPtr等)
+
---文字型 (char)
+
---ブール型 (bool)
+
--構造体
+
--列挙体
+
-''参照型''
+
--クラス
+
--インターフェイス
+
--デリゲート
+
--オブジェクト型 (object)
+
--文字列型 (string)
+
--配列
+

          
+
.NET Frameworkの型システムにおいてはint(System.Int32)やfloat(System.Single)などのプリミティブ型も構造体であることから、おおまかに「構造体・列挙体が値型、それ以外が参照型」と分類することができます。 [[Type.IsValueTypeプロパティ>#Type.IsValueType]]を参照することで実行時に型が値型かどうかを知ることができます。
+

          
+

          
+
*値型・参照型の挙動の違い
+
以下では値型・参照型の挙動の違いや扱う上での注意点、構造体とクラスの使い分けなどについて解説します。
+

          
+
**値渡し・参照渡し [#call_by_value]
+
メソッドの引数を値渡しする場合、値型の場合は代入の際と同様インスタンスの複製がメソッドに渡されるため、メソッド内で引数に変更を加えても呼び出し元の変数には反映されません。
+

          
+
一方参照型の場合、メソッドの引数には参照が渡されるため、呼び出し元の変数と同一のインスタンスを参照します。 そのため、メソッド内で引数に変更を加えると呼び出し元の変数に反映されます。
+

          
+
#column
+
#tabpage(codelang=cs,container-title=値型の値渡し)
+
#code{{
+
using System;
+

          
+
// 構造体(値型)
+
struct ValType {
+
  public int ID;
+

          
+
  public ValType(int id)
+
  {
+
    ID = id;
+
  }
+
}
+

          
+
class Sample {
+
  static void Method(ValType v)
+
  {
+
    // 引数で渡される値に変更を加える
+
    v.ID = 2;
+
  }
+

          
+
  static void Main()
+
  {
+
    ValType v = new ValType(1);
+

          
+
    Console.WriteLine("v.ID = {0}", v.ID);
+

          
+
    Method(v);
+

          
+
    Console.WriteLine("v.ID = {0}", v.ID);
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+

          
+
' 構造体(値型)
+
Structure ValType
+
  Public ID As Integer
+

          
+
  Public Sub New(ByVal id As Integer)
+
    Me.ID = id
+
  End Sub
+
End Structure
+

          
+
Class Sample
+
  Shared Sub Method(ByVal v As ValType)
+
    ' 引数で渡される値に変更を加える
+
    v.ID = 2
+
  End Sub
+

          
+
  Shared Sub Main()
+
    Dim v As New ValType(1)
+

          
+
    Console.WriteLine("v.ID = {0}", v.ID)
+

          
+
    Method(v)
+

          
+
    Console.WriteLine("v.ID = {0}", v.ID)
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
v.ID = 1
+
v.ID = 1
+
}}
+

          
+
#column
+

          
+
#tabpage(codelang=cs,container-title=参照型の値渡し)
+
#code{{
+
using System;
+

          
+
// クラス(参照型)
+
class RefType {
+
  public int ID;
+

          
+
  public RefType(int id)
+
  {
+
    ID = id;
+
  }
+
}
+

          
+
class Sample {
+
  static void Method(RefType r)
+
  {
+
    // 引数で渡されるインスタンスに変更を加える
+
    r.ID = 2;
+
  }
+

          
+
  static void Main()
+
  {
+
    RefType r = new RefType(1);
+

          
+
    Console.WriteLine("r.ID = {0}", r.ID);
+

          
+
    Method(r);
+

          
+
    Console.WriteLine("r.ID = {0}", r.ID);
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+

          
+
' クラス(参照型)
+
Class RefType
+
  Public ID As Integer
+

          
+
  Public Sub New(ByVal id As Integer)
+
    Me.ID = id
+
  End Sub
+
End Class
+

          
+
Class Sample
+
  Shared Sub Method(ByVal r As RefType)
+
    ' 引数で渡されるインスタンスに変更を加える
+
    r.ID = 2
+
  End Sub
+

          
+
  Shared Sub Main()
+
    Dim r As New RefType(1)
+

          
+
    Console.WriteLine("r.ID = {0}", r.ID)
+

          
+
    Method(r)
+

          
+
    Console.WriteLine("r.ID = {0}", r.ID)
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
r.ID = 1
+
r.ID = 2
+
}}
+
#column-end
+

          
+
メソッドの引数を参照渡し(ref/ByRef)する場合は、値型の場合でも引数に指定したインスタンスを参照することになるため、メソッド内で引数に変更を加えると参照型の場合と同様に呼び出し元の変数に反映されます。
+

          
+
#column
+
#tabpage(codelang=cs,container-title=値型の参照渡し)
+
#code{{
+
using System;
+

          
+
// 構造体(値型)
+
struct ValType {
+
  public int ID;
+

          
+
  public ValType(int id)
+
  {
+
    ID = id;
+
  }
+
}
+

          
+
class Sample {
+
  static void Method(ref ValType v)
+
  {
+
    // 引数で渡される値に変更を加える
+
    v.ID = 2;
+
  }
+

          
+
  static void Main()
+
  {
+
    ValType v = new ValType(1);
+

          
+
    Console.WriteLine("v.ID = {0}", v.ID);
+

          
+
    Method(ref v);
+

          
+
    Console.WriteLine("v.ID = {0}", v.ID);
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+

          
+
' 構造体(値型)
+
Structure ValType
+
  Public ID As Integer
+

          
+
  Public Sub New(ByVal id As Integer)
+
    Me.ID = id
+
  End Sub
+
End Structure
+

          
+
Class Sample
+
  Shared Sub Method(ByRef v As ValType)
+
    ' 引数で渡される値に変更を加える
+
    v.ID = 2
+
  End Sub
+

          
+
  Shared Sub Main()
+
    Dim v As New ValType(1)
+

          
+
    Console.WriteLine("v.ID = {0}", v.ID)
+

          
+
    Method(v)
+

          
+
    Console.WriteLine("v.ID = {0}", v.ID)
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
v.ID = 1
+
v.ID = 2
+
}}
+

          
+
#column
+

          
+
#tabpage(codelang=cs,container-title=参照型の参照渡し)
+
#code{{
+
using System;
+

          
+
// クラス(参照型)
+
class RefType {
+
  public int ID;
+

          
+
  public RefType(int id)
+
  {
+
    ID = id;
+
  }
+
}
+

          
+
class Sample {
+
  static void Method(ref RefType r)
+
  {
+
    // 引数で渡されるインスタンスに変更を加える
+
    r.ID = 2;
+
  }
+

          
+
  static void Main()
+
  {
+
    RefType r = new RefType(1);
+

          
+
    Console.WriteLine("r.ID = {0}", r.ID);
+

          
+
    Method(ref r);
+

          
+
    Console.WriteLine("r.ID = {0}", r.ID);
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+

          
+
' クラス(参照型)
+
Class RefType
+
  Public ID As Integer
+

          
+
  Public Sub New(ByVal id As Integer)
+
    Me.ID = id
+
  End Sub
+
End Class
+

          
+
Class Sample
+
  Shared Sub Method(ByRef r As RefType)
+
    ' 引数で渡されるインスタンスに変更を加える
+
    r.ID = 2
+
  End Sub
+

          
+
  Shared Sub Main()
+
    Dim r As New RefType(1)
+

          
+
    Console.WriteLine("r.ID = {0}", r.ID)
+

          
+
    Method(r)
+

          
+
    Console.WriteLine("r.ID = {0}", r.ID)
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
r.ID = 1
+
r.ID = 2
+
}}
+
#column-end
+

          
+
関連:[[programming/netfx/tips/set_struct_field_using_reflection]]
+

          
+

          
+
**値型のプロパティ・インデクサ [#value_type_property]
+
値型では代入時にコピーが作成されますが、値型のプロパティやインデクサから値を取得しようとする場合も同様にコピーが作成されます。 値型のプロパティ・インデクサはインスタンスそのものではなくインスタンスのコピーを返すことから、直接インスタンスを変更することができません。
+

          
+
そのため、次の例のように値型のプロパティを直接変更しようとするとコンパイルエラーとなります。 参照型では変更しようとするインスタンスを参照によって取得することができるため、コンパイルエラーとはなりません。
+

          
+
#column
+
#tabpage(codelang=cs,container-title=値型のプロパティに対する変更)
+
#code{{
+
using System;
+

          
+
// 構造体(値型)
+
struct ValType {
+
  public int ID;
+
}
+

          
+
class C {
+
  ValType v = new ValType();
+

          
+
  // 値型のプロパティ
+
  public ValType V {
+
    get { return v; }
+
    set { v = value; }
+
  }
+

          
+
  public override string ToString()
+
  {
+
    return string.Format("v.ID = {0}", v.ID);
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    C c = new C();
+

          
+
    Console.WriteLine(c);
+

          
+
    // 値型のプロパティに変更を加えようとする
+
    c.V.ID = 3;
+
    // error CS1612: 変数ではないため、
+
    // 'C.V' の戻り値を変更できません。
+

          
+
    Console.WriteLine(c);
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+

          
+
' 構造体(値型)
+
Structure ValType
+
  Public ID As Integer
+
End Structure
+

          
+
Class C
+
  Dim _v As ValType = New ValType()
+

          
+
  ' 値型のプロパティ
+
  Public Property V() As ValType
+
    Get
+
      Return _v
+
    End Get
+
    Set (ByVal value As ValType)
+
      _v = value
+
    End Set
+
  End Property
+

          
+
  Public Overrides Function ToString() As String
+
    Return String.Format("v.ID = {0}", v.ID)
+
  End Function
+
End Class
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim c As New C()
+

          
+
    Console.WriteLine(c)
+

          
+
    ' 値型のプロパティに変更を加えようとする
+
    c.V.ID = 3
+
    ' error BC30068: Expression は値であるため、
+
    ' 代入式のターゲットにすることはできません。
+

          
+
    Console.WriteLine(c)
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#column
+

          
+
#tabpage(codelang=cs,container-title=参照型のプロパティに対する変更)
+
#code{{
+
using System;
+

          
+
// クラス(参照型)
+
class RefType {
+
  public int ID;
+
}
+

          
+
class C {
+
  RefType r = new RefType();
+

          
+
  // 参照型のプロパティ
+
  public RefType R {
+
    get { return r; }
+
    /* setterは必要ない */
+
  }
+

          
+
  public override string ToString()
+
  {
+
    return string.Format("r.ID = {0}", r.ID);
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    C c = new C();
+

          
+
    Console.WriteLine(c);
+

          
+
    // 参照型のプロパティに変更を加える
+
    c.R.ID = 3;
+
    // コンパイルエラーとはならない
+

          
+
    Console.WriteLine(c);
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+

          
+
' クラス(参照型)
+
Class RefType
+
  Public ID As Integer
+
End Class
+

          
+
Class C
+
  Dim _r As RefType = New RefType()
+

          
+
  ' 参照型のプロパティ
+
  Public ReadOnly Property R() As RefType
+
    Get
+
      Return _r
+
    End Get
+
    '
+
    ' setterは必要ない
+
    '
+
  End Property
+

          
+
  Public Overrides Function ToString() As String
+
    Return String.Format("r.ID = {0}", r.ID)
+
  End Function
+
End Class
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim c As New C()
+

          
+
    Console.WriteLine(c)
+

          
+
    ' 参照型のプロパティに変更を加える
+
    c.R.ID = 3
+
    ' コンパイルエラーとはならない
+

          
+
    Console.WriteLine(c)
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
r.ID = 0
+
r.ID = 3
+
}}
+
#column-end
+

          
+
コンパイルエラーとならないようにするには、次の例のように一旦プロパティの値を一時変数に代入してコピーし、変更を加えた後にプロパティに再代入します。
+

          
+
#tabpage(codelang=cs,container-title=一時変数を使って値型のプロパティを変更する例)
+
#code{{
+
class Sample {
+
  static void Main()
+
  {
+
    C c = new C();
+

          
+
    Console.WriteLine(c);
+

          
+
    // 現在のプロパティの値を一時変数にコピーする
+
    ValType v = c.V;
+

          
+
    // 一時変数に代入したインスタンスに対して変更を加える
+
    v.ID = 3;
+

          
+
    // 変更した一時変数のインスタンスをプロパティにコピーする
+
    c.V = v;
+

          
+
    Console.WriteLine(c);
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Class Sample
+
  Shared Sub Main()
+
    Dim c As New C()
+

          
+
    Console.WriteLine(c)
+

          
+
    ' 現在のプロパティの値を一時変数にコピーする
+
    Dim v As ValType = c.V
+

          
+
    ' 一時変数に代入したインスタンスに対して変更を加える
+
    v.ID = 3
+

          
+
    ' 変更した一時変数のインスタンスをプロパティにコピーする
+
    c.V = v
+

          
+
    Console.WriteLine(c)
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
v.ID = 0
+
v.ID = 3
+
}}
+

          
+
値型配列の要素を直接変更することはできますが、値型のインデクサの場合もプロパティと同様に直接変更することはできません。
+

          
+
#tabpage(codelang=cs,container-title=値型のインデクサに対する変更)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+

          
+
// 構造体(値型)
+
struct ValType {
+
  public int ID;
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    ValType[] arr = new ValType[1];
+

          
+
    // 配列の要素を変更
+
    arr[0].ID = 3;
+

          
+
    List<ValType> list = new List<ValType>(new ValType[1]);
+

          
+
    // インデクサを使用して要素を変更
+
    list[0].ID = 3;
+
    // error CS1612: 変数ではないため、'System.Collections.Generic.List<ValType>.this[int]'の戻り値を変更できません。
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+
Imports System.Collections.Generic
+

          
+
' 構造体(値型)
+
Structure ValType
+
  Public ID As Integer
+
End Structure
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim arr(0) As ValType
+

          
+
    ' 配列の要素を変更
+
    arr(0).ID = 3
+

          
+
    Dim list As New List(Of ValType)()
+

          
+
    list.Add(New ValType())
+

          
+
    ' インデクサを使用して要素を変更
+
    list(0).ID = 3
+
    ' error BC30068: Expression は値であるため、代入式のターゲットにすることはできません。
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
さらに、インスタンスに変更を加えるようなメソッドを呼び出す場合も同様の問題が発生します。 以下の例ではプロパティで取得した値型インスタンスのメソッドを呼び出していますが、メソッド呼び出しで変更されるのはあくまで取得によってコピーされたインスタンスであって、元のインスタンスには一切影響しないため、一見するとメソッド呼び出しによる変更が反映されないような動作となります。
+

          
+
#column
+
#tabpage(codelang=cs,container-title=値型のプロパティとメソッド呼び出し)
+
#code{{
+
using System;
+

          
+
// 構造体(値型)
+
struct ValType {
+
  public int ID;
+

          
+
  // IDフィールドの値を設定するメソッド
+
  public void SetID(int newID)
+
  {
+
    ID = newID;
+
  }
+
}
+

          
+
class C {
+
  ValType v = new ValType();
+

          
+
  // 値型のプロパティ
+
  public ValType V {
+
    get { return v; }
+
  }
+

          
+
  public override string ToString()
+
  {
+
    return string.Format("v.ID = {0}", v.ID);
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    C c = new C();
+

          
+
    Console.WriteLine(c);
+

          
+
    // 値型のプロパティに変更を加えようとする
+
    c.V.SetID(3);
+

          
+
    // 意図に反して変更が反映されないように見える
+
    Console.WriteLine(c);
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+

          
+
' 構造体(値型)
+
Structure ValType
+
  Public ID As Integer
+

          
+
  ' IDフィールドの値を設定するメソッド
+
  Public Sub SetID(ByVal newID As Integer)
+
    ID = newID
+
  End Sub
+
End Structure
+

          
+
Class C
+
  Dim _v As ValType = New ValType()
+

          
+
  Public ReadOnly Property V As ValType
+
    Get
+
      Return _v
+
    End Get
+
  End Property
+

          
+
  Public Overrides Function ToString() As String
+
    Return String.Format("v.ID = {0}", v.ID)
+
  End Function
+
End Class
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim c As New C()
+

          
+
    Console.WriteLine(c)
+

          
+
    ' 値型のプロパティに変更を加えようとする
+
    c.V.SetID(3)
+

          
+
    ' 意図に反して変更が反映されないように見える
+
    Console.WriteLine(c)
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
v.ID = 0
+
v.ID = 0
+
}}
+
#column
+

          
+
#tabpage(codelang=cs,container-title=参照型のプロパティとメソッド呼び出し)
+
#code{{
+
using System;
+

          
+
// クラス(参照型)
+
class RefType {
+
  public int ID;
+

          
+
  // IDフィールドの値を設定するメソッド
+
  public void SetID(int newID)
+
  {
+
    ID = newID;
+
  }
+
}
+

          
+
class C {
+
  RefType r = new RefType();
+

          
+
  // 参照型のプロパティ
+
  public RefType R {
+
    get { return r; }
+
  }
+

          
+
  public override string ToString()
+
  {
+
    return string.Format("r.ID = {0}", r.ID);
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    C c = new C();
+

          
+
    Console.WriteLine(c);
+

          
+
    // 参照型のプロパティに変更を加える
+
    c.R.SetID(3);
+

          
+
    // 意図したとおり変更が反映される
+
    Console.WriteLine(c);
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+

          
+
' クラス(参照型)
+
Class RefType
+
  Public ID As Integer
+

          
+
  ' IDフィールドの値を設定するメソッド
+
  Public Sub SetID(ByVal newID As Integer)
+
    ID = newID
+
  End Sub
+
End Class
+

          
+
Class C
+
  Dim _r As RefType = New RefType()
+

          
+
  Public ReadOnly Property R As RefType
+
    Get
+
      Return _r
+
    End Get
+
  End Property
+

          
+
  Public Overrides Function ToString() As String
+
    Return String.Format("r.ID = {0}", r.ID)
+
  End Function
+
End Class
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim c As New C()
+

          
+
    Console.WriteLine(c)
+

          
+
    ' 参照型のプロパティに変更を加える
+
    c.R.SetID(3)
+

          
+
    ' 意図したとおり変更が反映される
+
    Console.WriteLine(c)
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
r.ID = 0
+
r.ID = 3
+
}}
+
#column-end
+

          
+
この場合も、先の例と同様に一時変数に代入してからメソッド呼び出しを行うことで変更を反映させることができます。
+

          
+
一見すると予想に反する動作であるにも関わらず、このようなコードはコンパイルエラーとはならないため注意する必要があります。 インデクサを多用するListやDictionaryなどのジェネリックコレクションで値型を扱う場合にはこういった問題に遭遇しやすいので注意が必要です。 (関連:[[programming/netfx/collections/2_generic_1_list#modify_valuetype_element]]、[[programming/netfx/collections/2_generic_2_dictionary#modify_valuetype_element]])
+

          
+

          
+
**同値性・同一性の比較 [#equality]
+
&msdn(netfx,member,System.Object.Equals){Equalsメソッド};を使うと、二つのインスタンスが''等しいかどうか''の比較が行われます。 Equalsメソッドのデフォルトの動作は値型と参照型で次のように異なります。
+

          
+
:値型の場合|二つの値が''ビット単位で等しい''場合にtrueとなる
+
:参照型の場合|二つの参照が''同一のインスタンスを参照している''場合にtrueとなる
+

          
+
つまり、値型では''同値性の比較''が行われ、参照型では''同一性の比較''が行われます。
+

          
+
#column
+
#tabpage(codelang=cs,container-title=値型の比較)
+
#code{{
+
using System;
+

          
+
// 構造体(値型)
+
struct ValType {
+
  public int ID;
+

          
+
  public ValType(int id)
+
  {
+
    ID = id;
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    ValType a = new ValType(1);
+
    ValType b = a;
+
    ValType c = new ValType(2);
+

          
+
    b.ID = 2;
+

          
+
    // Equalsメソッドで3つの変数を比較する
+
    Console.WriteLine("a.Equals(b) : {0}", a.Equals(b));
+
    Console.WriteLine("a.Equals(c) : {0}", a.Equals(c));
+
    Console.WriteLine("b.Equals(c) : {0}", b.Equals(c));
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+

          
+
' 構造体(値型)
+
Structure ValType
+
  Public ID As Integer
+

          
+
  Public Sub New(ByVal id As Integer)
+
    Me.ID = id
+
  End Sub
+
End Structure
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim a As New ValType(1)
+
    Dim b As ValType = a
+
    Dim c As New ValType(2)
+

          
+
    b.ID = 2
+

          
+
    ' Equalsメソッドで3つの変数を比較する
+
    Console.WriteLine("a.Equals(b) : {0}", a.Equals(b))
+
    Console.WriteLine("a.Equals(c) : {0}", a.Equals(c))
+
    Console.WriteLine("b.Equals(c) : {0}", b.Equals(c))
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
a.Equals(b) : False
+
a.Equals(c) : False
+
b.Equals(c) : True
+
}}
+
#column
+

          
+
#tabpage(codelang=cs,container-title=参照型の比較)
+
#code{{
+
using System;
+

          
+
// クラス(参照型)
+
class RefType {
+
  public int ID;
+

          
+
  public RefType(int id)
+
  {
+
    ID = id;
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    RefType a = new RefType(1);
+
    RefType b = a;
+
    RefType c = new RefType(2);
+

          
+
    b.ID = 2;
+

          
+
    // Equalsメソッドで3つの変数を比較する
+
    Console.WriteLine("a.Equals(b) : {0}", a.Equals(b));
+
    Console.WriteLine("a.Equals(c) : {0}", a.Equals(c));
+
    Console.WriteLine("b.Equals(c) : {0}", b.Equals(c));
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+

          
+
' クラス(参照型)
+
Class RefType
+
  Public ID As Integer
+

          
+
  Public Sub New(ByVal id As Integer)
+
    Me.ID = id
+
  End Sub
+
End Class
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim a As New RefType(1)
+
    Dim b As RefType = a
+
    Dim c As New RefType(2)
+

          
+
    b.ID = 2
+

          
+
    ' Equalsメソッドで3つの変数を比較する
+
    Console.WriteLine("a.Equals(b) : {0}", a.Equals(b))
+
    Console.WriteLine("a.Equals(c) : {0}", a.Equals(c))
+
    Console.WriteLine("b.Equals(c) : {0}", b.Equals(c))
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
a.Equals(b) : True
+
a.Equals(c) : False
+
b.Equals(c) : False
+
}}
+
#column-end
+

          
+
[[EqualsメソッドをオーバーライドしたりIEquatable<T>インターフェイスを実装する>programming/netfx/comparison/1_equation]]ことでEqualsメソッドの動作を変えることができます。 例えば、[[String.Equalsメソッド>programming/netfx/string/2_1_comparison#String.CompareTo]]が文字列の同値性の比較を行うように、参照型でも同値性の比較を行うように実装することができます。 このほか、任意の型同士で参照の比較(同一性の比較)を行いたい場合は、&msdn(netfx,member,System.Object.ReferenceEquals){Object.ReferenceEquals};メソッドを使うことができます。
+

          
+

          
+
**ボックス化 [#boxing]
+
値型のインスタンスをobject型変数に代入する場合、スタックに配置されている値型インスタンスの複製が作成され、object型変数に''箱詰め''した上でヒープに配置されます。 これを''ボックス化''(boxing)と呼びます。 ボックス化の際インスタンスの複製が作成されるため、元のインスタンスとボックス化されたインスタンスは別々のものとなります。 そのため、元のインスタンスに変更を加えてもボックス化されたインスタンスには影響しません。
+

          
+
参照型のインスタンスをobject型変数に代入する場合は単にアップキャストとなるだけで、ボックス化は行われません。 参照がobject型変数に代入されるだけとなるため、当然object型に代入されるインスタンスは元のインスタンスと同一のものとなります。
+

          
+
#column
+
#tabpage(codelang=cs,container-title=値型インスタンスのボックス化)
+
#code{{
+
using System;
+

          
+
// 構造体(値型)
+
struct ValType {
+
  public int ID;
+

          
+
  public ValType(int id)
+
  {
+
    ID = id;
+
  }
+

          
+
  public override string ToString()
+
  {
+
    return string.Format("ID = {0}", ID);
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    ValType v = new ValType(1);
+
    object o = v; // ボックス化
+

          
+
    Console.WriteLine("v:{0}, o:{1}", v, o);
+

          
+
    v.ID = 2;
+

          
+
    Console.WriteLine("v:{0}, o:{1}", v, o);
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+

          
+
' 構造体(値型)
+
Structure ValType
+
  Public ID As Integer
+

          
+
  Public Sub New(ByVal id As Integer)
+
    Me.ID = id
+
  End Sub
+

          
+
  Public Overrides Function ToString() As String
+
    Return String.Format("ID = {0}", ID)
+
  End Function
+
End Structure
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim v As New ValType(1)
+
    Dim o As Object = v ' ボックス化
+

          
+
    Console.WriteLine("v:{0}, o:{1}", v, o)
+

          
+
    v.ID = 2
+

          
+
    Console.WriteLine("v:{0}, o:{1}", v, o)
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
v:ID = 1, o:ID = 1
+
v:ID = 2, o:ID = 1
+
}}
+

          
+
#column
+

          
+
#tabpage(codelang=cs,container-title=参照型インスタンスのアップキャスト)
+
#code{{
+
using System;
+

          
+
// クラス(参照型)
+
class RefType {
+
  public int ID;
+

          
+
  public RefType(int id)
+
  {
+
    ID = id;
+
  }
+

          
+
  public override string ToString()
+
  {
+
    return string.Format("ID = {0}", ID);
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    RefType r = new RefType(1);
+
    object o = r; // アップキャスト
+

          
+
    Console.WriteLine("r:{0}, o:{1}", r, o);
+

          
+
    r.ID = 2;
+

          
+
    Console.WriteLine("r:{0}, o:{1}", r, o);
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+

          
+
' クラス(参照型)
+
Class RefType
+
  Public ID As Integer
+

          
+
  Public Sub New(ByVal id As Integer)
+
    Me.ID = id
+
  End Sub
+

          
+
  Public Overrides Function ToString() As String
+
    Return String.Format("ID = {0}", ID)
+
  End Function
+
End Class
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim r As New RefType(1)
+
    Dim o As Object = r ' アップキャスト
+

          
+
    Console.WriteLine("r:{0}, o:{1}", r, o)
+

          
+
    r.ID = 2
+

          
+
    Console.WriteLine("r:{0}, o:{1}", r, o)
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
r:ID = 1, o:ID = 1
+
r:ID = 2, o:ID = 2
+
}}
+
#column-end
+

          
+
逆に、object型変数から値型のインスタンスを取り出す際には''ボックス化解除''(unboxing)が行われます。 ボックス化解除では、object型に''箱詰め''されている値型インスタンスを取り出して値型変数に代入します。
+

          
+
object型変数への代入だけでなく、値型が実装するインターフェイス型へ代入する場合にもボックス化が行われます。
+

          
+
ボックス化ではインスタンスの複製が作成されるため、参照型のキャストと比べると若干コストのある操作となります。 値型のボックス化・ボックス化解除と参照型のアップキャスト・ダウンキャストの速度を比較すると次のようになります。
+

          
+
#column
+
#code(cs){{
+
using System;
+
using System.Diagnostics;
+

          
+
// 構造体(値型)
+
struct ValType {}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    for (var c = 0; c < 5; c++) {
+
      var v = new ValType();
+
      object o;
+

          
+
      var sw = Stopwatch.StartNew();
+

          
+
      for (var i = 0; i < 10 * 1000 * 1000; i++) {
+
        o = v;          // ボックス化
+
        v = (ValType)o; // ボックス化解除
+
      }
+

          
+
      Console.WriteLine(sw.Elapsed);
+
    }
+
  }
+
}
+
}}
+

          
+
#prompt(.NET Framework 4.0での実行結果){{
+
00:00:00.0153980
+
00:00:00.0149546
+
00:00:00.0161484
+
00:00:00.0174094
+
00:00:00.0154963
+
}}
+

          
+
#prompt(Mono 2.10.9での実行結果){{
+
00:00:00.0384817
+
00:00:00.0356860
+
00:00:00.0328524
+
00:00:00.0318040
+
00:00:00.0316627
+
}}
+
#column
+
#code(cs){{
+
using System;
+
using System.Diagnostics;
+

          
+
// クラス(参照型)
+
class RefType {}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    for (var c = 0; c < 5; c++) {
+
      var r = new RefType();
+
      object o;
+

          
+
      var sw = Stopwatch.StartNew();
+

          
+
      for (var i = 0; i < 10 * 1000 * 1000; i++) {
+
        o = r;          // アップキャスト
+
        r = (RefType)o; // ダウンキャスト
+
      }
+

          
+
      Console.WriteLine(sw.Elapsed);
+
    }
+
  }
+
}
+
}}
+

          
+
#prompt(.NET Framework 4.0での実行結果){{
+
00:00:00.0012479
+
00:00:00.0012820
+
00:00:00.0012420
+
00:00:00.0012590
+
00:00:00.0012509
+
}}
+

          
+
#prompt(Mono 2.10.9での実行結果){{
+
00:00:00.0027001
+
00:00:00.0026039
+
00:00:00.0025005
+
00:00:00.0025088
+
00:00:00.0026163
+
}}
+
#column-end
+

          
+
非ジェネリックコレクションで値型を扱う場合、コレクションへの格納・取り出しを行う度にボックス化・ボックス化解除されることになります。 そのため、パフォーマンスの観点からも[[ArrayListなどの非ジェネリックコレクション>programming/netfx/collections/1_nongeneric_0_abstract]]よりもボックス化・ボックス化解除が発生しない[[Listなどのジェネリックコレクション>programming/netfx/collections/2_generic_0_abstract]]を使うことが推奨されます。
+

          
+

          
+
**代入の速度 [#substitution_cost]
+
値型の代入ではインスタンスの複製が行われるため、型のサイズが大きくなるほど代入にかかるコストは大きくなります。 参照型ではインスタンスの複製は行われないため、型のサイズによらず代入にかかるコストは一定となります。
+

          
+
#column
+
#code(cs,計4×1バイトのフィールドを持つ値型の代入){{
+
using System;
+
using System.Diagnostics;
+

          
+
// 構造体(値型)
+
struct ValType {
+
  public int Field1;
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    for (var c = 0; c < 5; c++) {
+
      ValType v1 = new ValType();
+
      ValType v2;
+

          
+
      var sw = Stopwatch.StartNew();
+

          
+
      for (var i = 0; i < 100 * 1000 * 1000; i++) {
+
        v2 = v1; // 代入
+
      }
+

          
+
      Console.WriteLine(sw.Elapsed);
+
    }
+
  }
+
}
+
}}
+

          
+
#prompt(.NET Framework 4.0での実行結果){{
+
00:00:00.1371791
+
00:00:00.1295024
+
00:00:00.1292611
+
00:00:00.1309864
+
00:00:00.0770210
+
}}
+

          
+
#prompt(Mono 2.10.9での実行結果){{
+
00:00:00.0967827
+
00:00:00.0875368
+
00:00:00.0853119
+
00:00:00.0854036
+
00:00:00.0864029
+
}}
+
#column
+
#code(cs,計4×1バイトのフィールドを持つ参照型の代入){{
+
using System;
+
using System.Diagnostics;
+

          
+
// クラス(参照型)
+
class RefType {
+
  public int Field1;
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    for (var c = 0; c < 5; c++) {
+
      RefType r1 = new RefType();
+
      RefType r2;
+

          
+
      var sw = Stopwatch.StartNew();
+

          
+
      for (var i = 0; i < 100 * 1000 * 1000; i++) {
+
        r2 = r1; // 代入
+
      }
+

          
+
      Console.WriteLine(sw.Elapsed);
+
    }
+
  }
+
}
+
}}
+

          
+
#prompt(.NET Framework 4.0での実行結果){{
+
00:00:00.1231474
+
00:00:00.1206678
+
00:00:00.0887434
+
00:00:00.0872789
+
00:00:00.0861327
+
}}
+

          
+
#prompt(Mono 2.10.9での実行結果){{
+
00:00:00.1159542
+
00:00:00.0857808
+
00:00:00.0854572
+
00:00:00.0859070
+
00:00:00.0863372
+
}}
+
#column-end
+

          
+
#column
+
#code(cs,計4×8バイトのフィールドを持つ値型の代入){{
+
using System;
+
using System.Diagnostics;
+

          
+
// 構造体(値型)
+
struct ValType {
+
  public int Field1;
+
  public int Field2;
+
  public int Field3;
+
  public int Field4;
+
  public int Field5;
+
  public int Field6;
+
  public int Field7;
+
  public int Field8;
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    for (var c = 0; c < 5; c++) {
+
      ValType v1 = new ValType();
+
      ValType v2;
+

          
+
      var sw = Stopwatch.StartNew();
+

          
+
      for (var i = 0; i < 100 * 1000 * 1000; i++) {
+
        v2 = v1; // 代入
+
      }
+

          
+
      Console.WriteLine(sw.Elapsed);
+
    }
+
  }
+
}
+
}}
+

          
+
#prompt(.NET Framework 4.0での実行結果){{
+
00:00:00.1145360
+
00:00:00.1139195
+
00:00:00.0907852
+
00:00:00.0858125
+
00:00:00.0871278
+
}}
+

          
+
#prompt(Mono 2.10.9での実行結果){{
+
00:00:00.2255986
+
00:00:00.1306481
+
00:00:00.1284407
+
00:00:00.1298623
+
00:00:00.1288758
+
}}
+
#column
+
#code(cs,計4×8バイトのフィールドを持つ参照型の代入){{
+
using System;
+
using System.Diagnostics;
+

          
+
// クラス(参照型)
+
class RefType {
+
  public int Field1;
+
  public int Field2;
+
  public int Field3;
+
  public int Field4;
+
  public int Field5;
+
  public int Field6;
+
  public int Field7;
+
  public int Field8;
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    for (var c = 0; c < 5; c++) {
+
      RefType r1 = new RefType();
+
      RefType r2;
+

          
+
      var sw = Stopwatch.StartNew();
+

          
+
      for (var i = 0; i < 100 * 1000 * 1000; i++) {
+
        r2 = r1; // 代入
+
      }
+

          
+
      Console.WriteLine(sw.Elapsed);
+
    }
+
  }
+
}
+
}}
+

          
+
#prompt(.NET Framework 4.0での実行結果){{
+
00:00:00.1113680
+
00:00:00.1196529
+
00:00:00.0888160
+
00:00:00.0872582
+
00:00:00.0874921
+
}}
+

          
+
#prompt(Mono 2.10.9での実行結果){{
+
00:00:00.1106691
+
00:00:00.0856765
+
00:00:00.0905668
+
00:00:00.0860375
+
00:00:00.0907923
+
}}
+
#column-end
+

          
+
サイズの大きい構造体の代入を多数行う必要がある場合、クラスに置き換えることを検討することでコストを下げることができます。 &msdn(netfx,id,ms229017){クラスまたは構造体の選択};(MSDN)では、構造体とクラスのどちらを選択するかという基準の1つに「サイズが16バイト未満かどうか」というガイドラインが設定されています。
+

          
+

          
+
**インスタンスの複製 (Object.MemberwiseClone) [#Object.MemberwiseClone]
+
&msdn(netfx,member,System.Object.MemberwiseClone){Object.MemberwiseCloneメソッド};を使ってインスタンスの複製を作成する際、フィールドが値型か参照型かによって複製時の動作が異なります。
+

          
+
値型のフィールドはビット単位での複製(''詳細コピー'')が行われるのに対し、参照型のフィールドは参照のみが複製されます(''簡易コピー'')。 そのため、MemberwiseCloneメソッドによるインスタンス複製後の参照型フィールドは、複製元の同一フィールドと同じインスタンスを参照することになります。
+

          
+
#tabpage(codelang=cs,container-title=MemberwiseCloneを使ってインスタンスを複製する例)
+
#code{{
+
using System;
+

          
+
// 構造体(値型)
+
struct ValType {
+
  public int ID;
+
}
+

          
+
// クラス(参照型)
+
class RefType {
+
  public int ID;
+
}
+

          
+
class C {
+
  // 値型フィールド
+
  public ValType V = new ValType();
+
  // 参照型フィールド
+
  public RefType R = new RefType();
+

          
+
  // インスタンスの複製を作成するメソッド
+
  public C Clone()
+
  {
+
    return (C)MemberwiseClone();
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    C c1 = new C();
+

          
+
    Console.WriteLine("c1.V.ID = {0}, c1.R.ID = {1}", c1.V.ID, c1.R.ID);
+

          
+
    // インスタンスを複製する
+
    C c2 = c1.Clone();
+

          
+
    // 複製後のインスタンスのフィールドに変更を加える
+
    c2.V.ID = 2;
+
    c2.R.ID = 2;
+

          
+
    Console.WriteLine("c1.V.ID = {0}, c1.R.ID = {1}", c1.V.ID, c1.R.ID);
+
    Console.WriteLine("c2.V.ID = {0}, c2.R.ID = {1}", c2.V.ID, c2.R.ID);
+

          
+
    Console.WriteLine("Object.ReferenceEquals(c1.R, c2.R) = {0}", Object.ReferenceEquals(c1.R, c2.R));
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+

          
+
' 構造体(値型)
+
Structure ValType
+
  Public ID As Integer
+
End Structure
+

          
+
' クラス(参照型)
+
Class RefType
+
  Public ID As Integer
+
End Class
+

          
+
Class C
+
  ' 値型フィールド
+
  Public V As ValType = New ValType()
+
  ' 参照型フィールド
+
  Public R As RefType = New RefType()
+

          
+
  ' インスタンスの複製を作成するメソッド
+
  Public Function Clone() As C
+
    Return DirectCast(MemberwiseClone(), C)
+
  End Function
+
End Class
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim c1 As New C()
+

          
+
    Console.WriteLine("c1.V.ID = {0}, c1.R.ID = {1}", c1.V.ID, c1.R.ID)
+

          
+
    ' インスタンスを複製する
+
    Dim c2 As C = c1.Clone()
+

          
+
    ' 複製後のインスタンスのフィールドに変更を加える
+
    c2.V.ID = 2
+
    c2.R.ID = 2
+

          
+
    Console.WriteLine("c1.V.ID = {0}, c1.R.ID = {1}", c1.V.ID, c1.R.ID)
+
    Console.WriteLine("c2.V.ID = {0}, c2.R.ID = {1}", c2.V.ID, c2.R.ID)
+

          
+
    Console.WriteLine("Object.ReferenceEquals(c1.R, c2.R) = {0}", Object.ReferenceEquals(c1.R, c2.R))
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
c1.V.ID = 0, c1.R.ID = 0
+
c1.V.ID = 0, c1.R.ID = 2
+
c2.V.ID = 2, c2.R.ID = 2
+
Object.ReferenceEquals(c1.R, c2.R) = True
+
}}
+

          
+
インスタンスの複製とMemberwiseCloneメソッドについては[[programming/netfx/cloning#Object.MemberwiseClone]]でも解説しています。
+

          
+

          
+
**デフォルト値 [#default_value]
+
初期値を指定しないでフィールドやローカル変数の宣言を行った場合、デフォルト値で初期化されます。 値型のデフォルト値は``0``(もしくは``0``に相当する値)で、参照型のデフォルト値はヌル参照(null/Nothing)です。 詳細:[[programming/netfx/basic_types/0_characteristics#DefaultValue]]
+

          
+

          
+
*値型か参照型か調べる [#Type.IsValueType]
+
実行時に型が値型か参照型かを調べるには、型情報(&msdn(netfx,type,System.Type){Type};)を取得し&msdn(netfx,member,System.Type.IsValueType){IsValueTypeプロパティ};を参照します。
+

          
+
#tabpage(codelang=cs,container-title=型情報を取得して値型か参照型か調べる)
+
#code{{
+
using System;
+

          
+
// 構造体(値型)
+
struct ValType {}
+

          
+
// クラス(参照型)
+
class RefType {}
+

          
+
class Sample {
+
  static void PrintType(Type t)
+
  {
+
    Console.WriteLine("{0,-20}: {1}", t.FullName, t.IsValueType ? "値型" : "参照型");
+
  }
+

          
+
  static void Main()
+
  {
+
    PrintType(typeof(int));
+
    PrintType(typeof(string));
+
    PrintType(typeof(object));
+
    PrintType(typeof(ValType)); // 構造体
+
    PrintType(typeof(RefType)); // クラス
+
    PrintType(typeof(int[])); // 配列
+
    PrintType(typeof(DayOfWeek)); // 列挙型
+
    PrintType(typeof(IDisposable)); // インターフェイス型
+
    PrintType(typeof(EventHandler)); // デリゲート型
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+

          
+
' 構造体(値型)
+
Structure ValType
+
End Structure
+

          
+
' クラス(参照型)
+
Class RefType
+
End Class
+

          
+
Class Sample
+
  Shared Sub PrintType(ByVal t As Type)
+
    Console.WriteLine("{0,-20}: {1}", t.FullName, If(t.IsValueType, "値型", "参照型"))
+
  End Sub
+

          
+
  Shared Sub Main()
+
    PrintType(GetType(Integer))
+
    PrintType(GetType(String))
+
    PrintType(GetType(Object))
+
    PrintType(GetType(ValType)) ' 構造体
+
    PrintType(GetType(RefType)) ' クラス
+
    PrintType(GetType(Integer())) ' 配列
+
    PrintType(GetType(DayOfWeek)) ' 列挙型
+
    PrintType(GetType(IDisposable)) ' インターフェイス型
+
    PrintType(GetType(EventHandler)) ' デリゲート型
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
System.Int32        : 値型
+
System.String       : 参照型
+
System.Object       : 参照型
+
ValType             : 値型
+
RefType             : 参照型
+
System.Int32[]      : 参照型
+
System.DayOfWeek    : 値型
+
System.IDisposable  : 参照型
+
System.EventHandler : 参照型
+
}}
+

          
+

          
+
*ジェネリック型の制約 [#generic_type_constraints]
+
ジェネリック型を定義する際、型パラメータに''制約''(constraints)を持たせることができます。 型パラメータに指定できる型を特定の基本型やインターフェイスを実装する型に限定するのと同様に、値型・参照型のみに限定させることもできます。
+

          
+
C#ではwhere句を使って``where T : struct``とすれば型パラメータ``T``を値型に、``where T : class``とすれば参照型に限定することができます。 VBではOf句を使って``Of T As Structure``とすれば``T``を値型に、``Of T As Class``とすれば参照型に限定できます。
+

          
+
#tabpage(codelang=cs,container-title=型パラメータに値型・参照型の制約を加える例)
+
#code{{
+
using System;
+
using System.Collections.ObjectModel;
+

          
+
// 型パラメータTを値型のみに限定したコレクション
+
class ValTypeCollection<T> : Collection<T> where T : struct {
+
}
+

          
+
// 型パラメータTを参照型のみに限定したコレクション
+
class RefTypeCollection<T> : Collection<T> where T : class {
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    // intは値型なので型を構築できる
+
    ValTypeCollection<int> intcol;
+

          
+
    // stringは参照型なので制約と一致せず、型を構築できない
+
    // (コンパイルエラーとなる)
+
    ValTypeCollection<string> strcol;
+
    // error CS0453: 型 'string' は、ジェネリック型のパラメーター 'T'、またはメソッド 'ValTypeCollection<T>' として使用するために、Null非許容の値型でなければなりません
+

          
+
    // floatは値型なので制約と一致せず、型を構築できない
+
    // (コンパイルエラーとなる)
+
    RefTypeCollection<float> fltcol;
+
    // error CS0452: 型 'float' は、ジェネリック型のパラメーター 'T'、またはメソッド 'RefTypeCollection<T>' として使用するために、参照型でなければなりません
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+
Imports System.Collections.ObjectModel
+

          
+
' 型パラメータTを値型のみに限定したコレクション
+
Class ValTypeCollection(Of T As Structure)
+
  Inherits Collection(Of T)
+
End Class
+

          
+
' 型パラメータTを参照型のみに限定したコレクション
+
Class RefTypeCollection(Of T As Class)
+
  Inherits Collection(Of T)
+
End Class
+

          
+
Class Sample
+
  Shared Sub Main()
+
    ' Integerは値型なので型を構築できる
+
    Dim intcol As ValTypeCollection(Of Integer)
+

          
+
    ' Stringは参照型なので制約と一致せず、型を構築できない
+
    ' (コンパイルエラーとなる)
+
    Dim strcol As ValTypeCollection(Of String)
+
    ' error BC32105: 型引数 'String' は型パラメーター 'T' の 'Structure' 制約を満たしていません。
+

          
+
    ' Singleは値型なので制約と一致せず、型を構築できない
+
    ' (コンパイルエラーとなる)
+
    Dim sngcol As RefTypeCollection(Of Single)
+
    ' error BC32106: 型引数 'Single' は型パラメーター 'T' の 'Class' 制約を満たしていません。
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+

          
+