C#とVBで、クラスや構造体のインスタンスを作成するときに同時にそのプロパティやフィールドの値を初期化する方法を解説する。
対象:Visual Studio 2008以降
クラスや構造体をnewしてから改行してあらためてプロパティやフィールドに値を設定するコードを書くのを面倒だと感じたことはないだろうか? C#/Visual Basic(VB)では、それが1行で書けるのだ。しかもそれには、簡潔に書ける以上のメリットもある。本稿では、そのオブジェクト初期化子の使い方とメリットを解説する。
オブジェクト初期化子は、コンストラクタの呼び出しの後ろに中かっこで囲ってプロパティやフィールドに値を設定するコードを書く(次のコード)。VBでは、中かっこの前にWithキーワードが、プロパティやフィールドの前に「.」が必要だ。
// クラスのインスタンス化とプロパティの設定(従来の書き方)
PointClass pc = new PointClass();
pc.X = 1;
pc.Y = 2;
// オブジェクト初期化子を使ってクラスのインスタンス化と同時にプロパティを設定
var pc1 = new PointClass() { X = 1, Y = 2 };
Console.WriteLine("PointClass: X={0}, Y={1}", pc1.X, pc1.Y);
// 出力:PointClass: X=1, Y=2
// オブジェクト初期化子の手前のかっこは省略できる
// なお、C#ではカンマが余分にあっても構わない
var pc2 = new PointClass { X = 3, Y = 4, };
' クラスのインスタンス化とプロパティの設定(従来の書き方)
Dim pc As PointClass = New PointClass()
With pc
.X = 1
.Y = 2
End With
' オブジェクト初期化子を使ってクラスのインスタンス化と同時にプロパティを設定
Dim pc1 = New PointClass() With {.X = 1, .Y = 2}
Console.WriteLine("PointClass: X={0}, Y={1}", pc1.X, pc1.Y)
' 出力:PointClass: X=1, Y=2
' オブジェクト初期化子の手前のかっこは省略できる
Dim pc2 = New PointClass With {.X = 3, .Y = 4}
なお、上のコードに登場するPointClassクラスは次のコードだ。ToStringメソッドが定義してあることを覚えておいてほしい(後ほどメリットの説明のところで使用する)。
public class PointClass
{
// プロパティ
public int X { get; set; }
public int Y { get; set; }
// ToStringメソッドのオーバーライド
public override string ToString()
{
return string.Format("PointClass.ToString(): X={0}, Y={1}", X, Y);
}
}
Public Class PointClass
' プロパティ
Private _x As Integer
Public Property X() As Integer
Get
Return _x
End Get
Set(ByVal value As Integer)
_x = value
End Set
End Property
Private _y As Integer
Public Property Y() As Integer
Get
Return _y
End Get
Set(ByVal value As Integer)
_y = value
End Set
End Property
' ToStringメソッドのオーバーライド
Public Overrides Function ToString() As String
Return String.Format("PointClass.ToString(): X={0}, Y={1}", X, Y)
End Function
End Class
オブジェクト初期化子は、クラスだけでなく構造体にも使える。また、プロパティだけでなくフィールドの初期化にも利用できる。ただし、null許容型には使えない(次のコード)。
// 構造体のインスタンス化と同時にフィールドを設定
var ps = new PointStruct { X = 7, Y = 8 };
Console.WriteLine("PointStruct: X={0}, Y={1}", ps.X, ps.Y);
// 出力:PointStruct: X=7, Y=8
// null許容型ではコンパイルエラーになる
//var ps = new Nullable<PointStruct>() { Value.X = 9, Value.Y = 10 };
' 構造体のインスタンス化と同時にフィールドを設定
Dim ps = New PointStruct With {.X = 7, .Y = 8}
Console.WriteLine("PointStruct: X={0}, Y={1}", ps.X, ps.Y)
' 出力:PointStruct: X=7, Y=8
' null許容型ではコンパイルエラーになる
'dim ps = new Nullable(Of PointStruct)() with { .Value.X = 9, .Value.Y = 10 }
なお、上のコードに登場するPointStruct構造体は次のコードだ。
public struct PointStruct
{
public int X;
public int Y;
}
Public Structure PointStruct
Public X As Integer
Public Y As Integer
End Structure
また、型名を指定しなければ、匿名型のインスタンスを生成できる(次のコード)。
var a = new { X = 9, Y = 10 };
Console.WriteLine("(匿名型): X={0}, Y={1}", a.X, a.Y);
// 出力:(匿名型): X=9, Y=10
Dim a = New With {.X = 9, .Y = 10}
Console.WriteLine("(匿名型): X={0}, Y={1}", a.X, a.Y)
' 出力:(匿名型): X=9, Y=10
オブジェクト初期化子を使うことで、上述したように簡潔な記述が可能だ。それだけでなく、値を設定した後でもメソッドチェーンにできるのだ。次のコードのように、プロパティを設定してからメソッドを呼び出すコードを1行に続けて書けるのである。
Console.WriteLine(new PointClass() { X = 5, Y = 6, }.ToString().ToUpper());
// 出力:POINTCLASS.TOSTRING(): X=5, Y=6
Console.WriteLine(New PointClass() With {.X = 5, .Y = 6}.ToString().ToUpper())
' 出力:POINTCLASS.TOSTRING(): X=5, Y=6
ところで、同様な効果は引数付きのコンストラクタでも得られる。自作のクラスで、オブジェクトの動作に必須のプロパティがある場合は、それらを引数付きのコンストラクタで受け取るようにすると、それが必須であることが明瞭になる。逆に、必須ではないプロパティまで引数付きのコンストラクタで受け取るようにしてしまうと、使う側を混乱させてしまうだろう。以前は明確さよりも利便性を優先して必須ではないプロパティまで引数として受け取るコンストラクタを書くこともあったものだが(筆者にも経験がある)、オブジェクト初期化子が使えるようになった今では必須のプロパティ値だけを引数付きのコンストラクタで受け取るようにすればよい。
なお、「インスタンス化と同時に」とタイトルに書いたが、正確にはインスタンス化が終わってからオブジェクト初期化子によるプロパティ設定が行われる。同じプロパティに対して引数付きのコンストラクタとオブジェクト初期化子の両方で値を設定した場合は、コンストラクタ内で設定したプロパティをオブジェクト初期化子によって上書きすることになる。
オブジェクト初期化子を使うと、インスタンス化と値の設定が1行で書けるし、その後に続けてメソッドチェーンにも書ける。Visual Studio 2008から使える機能なので、簡潔なコードを書くために役立ててほしい。
利用可能バージョン:Visual Studio 2008以降
カテゴリ:Visual Studio 2008 処理対象:言語構文
カテゴリ:C# 処理対象:言語構文
カテゴリ:Visual Basic .NET 処理対象:言語構文
関連TIPS:手軽にプロパティを実装するには?[C#、VS 2008、3.5]
関連TIPS:Dictionaryクラスを簡単に初期化するには?[C# 3.0]
Copyright© Digital Advantage Corp. All Rights Reserved.