連載
» 2016年02月24日 05時00分 UPDATE

.NET TIPS:構文:インスタンス化と同時にプロパティを設定するには?[C#/VB]

C#とVBで、クラスや構造体のインスタンスを作成するときに同時にそのプロパティやフィールドの値を初期化する方法を解説する。

[山本康彦,BluewaterSoft/Microsoft MVP for Windows Development]
.NET TIPS
Insider.NET

 

「.NET TIPS」のインデックス

連載目次

対象: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}

クラスのインスタンス化とプロパティの設定をするコード例(上:C#、下:VB)
本稿のコードは、Visual C# 2008 ExpressとVisual Basic 2008 Expressで動作を確認している。
初期化子を使うと、1行でクラスのインスタンス化とプロパティの設定が書けてしまうのだ。

 なお、上のコードに登場する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

先のコード例で使ったPointClassクラス(上:C#、下:VB)
Visual Basic 2008では自動実装プロパティを利用できないので、C#に比べて長いコードになっている。

構造体、フィールド、匿名型

 オブジェクト初期化子は、クラスだけでなく構造体にも使える。また、プロパティだけでなくフィールドの初期化にも利用できる。ただし、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 }

オブジェクト初期化子は構造体でもフィールドでも使える(上:C#、下:VB)
ただし、null許容型には使えない。

 なお、上のコードに登場するPointStruct構造体は次のコードだ。

public struct PointStruct
{
  public int X;
  public int Y;
}

Public Structure PointStruct
  Public X As Integer
  Public Y As Integer
End Structure

先のコード例で使ったPointStruct構造体(上:C#、下:VB)

 また、型名を指定しなければ、匿名型のインスタンスを生成できる(次のコード)。

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

匿名型のインスタンスはオブジェクト初期化子を使って生成する(上:C#、下:VB)

オブジェクト初期化子のメリット

 オブジェクト初期化子を使うことで、上述したように簡潔な記述が可能だ。それだけでなく、値を設定した後でもメソッドチェーンにできるのだ。次のコードのように、プロパティを設定してからメソッドを呼び出すコードを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

オブジェクト初期化子を使うとメソッドチェーンにできる(上:C#、下:VB)
インスタンス化して値を設定してから、続けてそのインスタンスのメソッドを呼び出せる。
PointClassクラスのToStringメソッドは、前述したコードのようにオーバーライドしてあるので、このような出力になる。

 ところで、同様な効果は引数付きのコンストラクタでも得られる。自作のクラスで、オブジェクトの動作に必須のプロパティがある場合は、それらを引数付きのコンストラクタで受け取るようにすると、それが必須であることが明瞭になる。逆に、必須ではないプロパティまで引数付きのコンストラクタで受け取るようにしてしまうと、使う側を混乱させてしまうだろう。以前は明確さよりも利便性を優先して必須ではないプロパティまで引数として受け取るコンストラクタを書くこともあったものだが(筆者にも経験がある)、オブジェクト初期化子が使えるようになった今では必須のプロパティ値だけを引数付きのコンストラクタで受け取るようにすればよい。

 なお、「インスタンス化と同時に」とタイトルに書いたが、正確にはインスタンス化が終わってからオブジェクト初期化子によるプロパティ設定が行われる。同じプロパティに対して引数付きのコンストラクタとオブジェクト初期化子の両方で値を設定した場合は、コンストラクタ内で設定したプロパティをオブジェクト初期化子によって上書きすることになる。

まとめ

 オブジェクト初期化子を使うと、インスタンス化と値の設定が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]


「.NET TIPS」のインデックス

.NET TIPS

Copyright© 1999-2017 Digital Advantage Corp. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。