連載:C# 3.0入門

第3回 varによる変数宣言とコレクション初期化子

株式会社ピーデー 川俣 晶
2008/06/13
Page1 Page2 Page3 Page4

コレクション初期化子

 配列ではないコレクションの初期化についても、C# 3.0は機能強化されている。

 例えば、List<T>クラスを用いたリストを作成する際、初期値を容易に指定することができる。1、2、3という3つの初期値を持つList<int>オブジェクトは、以下のように宣言することができる。

using System;
using System.Collections.Generic;

class Program
{
  static void Main(string[] args)
  {
    // コレクション初期化子の使用
    var list = new List<int>() { 1, 2, 3 };

    foreach (var i in list) Console.WriteLine(i);
    // 出力:
    // 1
    // 2
    // 3
  }
}
リスト12 コレクション初期化子の使用

 ここで、「new List<int>()」と書いてインスタンスを生成させた後に書かれた「{ 1, 2, 3 }」の部分がコレクション初期化子である。

 コレクション初期化子は、一種のシンタックス・シュガー(糖衣構文)であり、実際には列挙された値をAddメソッドで順次追加するコードがコンパイル時に生成される。そのため、このメソッドを実装し、それに加えてIEnumerableインターフェイス(System.Collections名前空間)を実装すれば、自作クラスでコレクション初期化子を利用することもできる。

 実際に、それを実現した例を以下に示す。

using System;
using System.Collections.Generic;

class WriteOnlyCollection<T> : IEnumerable<T>
{
  public void Add(T item)
  {
    Console.WriteLine("値{0}は虚無の空間に格納された。", item);
  }

  public IEnumerator<T> GetEnumerator()
  {
    throw new NotImplementedException();
  }

  System.Collections.IEnumerator
    System.Collections.IEnumerable.GetEnumerator()
  {
    throw new NotImplementedException();
  }
}

class Program
{
  static void Main(string[] args)
  {
    var collection = new WriteOnlyCollection<int>() { 1, 2, 3 };
    // 出力
    // 値1は虚無の空間に格納された。
    // 値2は虚無の空間に格納された。
    // 値3は虚無の空間に格納された。
  }
}
リスト13 コレクション初期化子の実装

 見てのとおり、Addメソッドは単に「Add」という名前のメソッドであればよい。特別なインターフェイスは不要である。古い資料には、「System.Collections.Generic.ICollection<T>インターフェイスのICollection<T>.Add(T) メソッドを使う」と書かれているものがあるが、これは誤りである。

 一方、IEnumerableインターフェイスを実装していないとコレクション初期化子は使用できないのだが、見てのとおり、機能の内容を実装する必要はまったくない。コレクション初期化子を利用するだけなら呼び出されることはないようである。

Dictionaryクラスとコレクション初期化子

 さて、ここで気になるのは、キーと値のペアを格納するDictionaryクラス(System.Collections.Generic名前空間)でこれを使用できるかである。この場合はキーと値の2つを単位に追加することになるが、もちろん同じ構文ではできない。

 しかし、これも以下のようにキーと値のペアをカンマで区切り、中カッコに入れて列挙すれば可能である。。

using System;
using System.Collections.Generic;

class Program
{
  static void Main(string[] args)
  {
    var t = new Dictionary<int, string>()
      {
        {16, "Hoshi"},
        {119, "Ban"},
      };
    foreach (int key in t.Keys)
    {
      Console.WriteLine("{0},{1}", key, t[key]);
    }
    // 出力:
    // 16,Hoshi
    // 119,Ban
  }
}
リスト14 コレクション初期化子をDictionaryクラスに使用する

引数が2つのAddメソッドとコレクション初期化子

 DictionaryクラスのようにAddメソッドに引数が2つ(キーと値)ある場合でも、コレクション初期化子は使用できる。それを自作コレクションクラスでも活用するには、どのように記述すればよいのだろうか?

 この場合、単にAddメソッドの引数を2つに増やせばよい。つまり、引数が2つのAddメソッドと、IEnumerableインターフェイスの実装という条件を満たしていればよい。

using System;
using System.Collections.Generic;

class SampleCollection<K, V> : IEnumerable<K>
{
  public void Add(K key, V val)
  {
    Console.WriteLine(
        "キー{0}値{1}は虚無の空間に格納された。", key,val);
  }

  public IEnumerator<K> GetEnumerator()
  {
    throw new NotImplementedException();
  }

  System.Collections.IEnumerator
    System.Collections.IEnumerable.GetEnumerator()
  {
    throw new NotImplementedException();
  }
}

class Program
{
  static void Main(string[] args)
  {
    var t = new SampleCollection<int, string>()
      {
        {16, "Hoshi"},
        {119, "Ban"},
      };
    // 出力:
    // キー16値Hoshiは虚無の空間に格納された。
    // キー119値Banは虚無の空間に格納された。
  }
}
リスト15 引数が2つのAddメソッドとコレクション初期化子

 ちなみに同様の書き方で、引数3個のAddメソッドも可能であることを確認した。

【コラム】定数の型を厳密に知ろう

 暗黙的に型指定されるローカル変数を使用する場合、定数値が厳密にどのような型を持っているか知る必要がある。例えば、123はint型であるが2147483648はuint型である。123がint型だったからといって、うかつに大きな整数を使って「var a = 2147483648;」のように初期化すると、int型の変数を宣言したつもりがuint型になっていたということもあり得るのである。

 また、符号なし整数の123を明示するには「123u」のように後ろに文字(サフィックス)を付けて表記する。例えば、「var a = 123;」の場合aはint型になるが、「var a = 123u;」の場合aはuint型になる。つまり、サフィックスによって、定数値の型を明示的に示すことができる。

 これらのルールを把握することは、暗黙的に型指定されるローカル変数を使用するうえで重要なポイントになるので、主立ったルールを、「C# 言語の仕様 2.4.4 リテラル」から抜粋して引用しておく。

  • サフィックスのないリテラルの場合は、int、uint、long、ulongのうち、その値を表すことができる最初の型になります。
  • リテラルのサフィックスがUまたはuの場合は、uintまたはulongのうち、その値を表すことができる最初の型になります。
  • リテラルのサフィックスがLまたはlの場合は、longまたはulongのうち、その値を表すことができる最初の型になります。
  • リテラルのサフィックスがUL、Ul、uL、ul、LU、Lu、lU、またはluの場合は、ulong型になります。
  • 実数リテラルのサフィックスがFまたはfの場合はfloat型です。例えば、1f、1.5f、1e10f、123.456Fなどのリテラルは、すべてfloat型です。
  • 実数リテラルのサフィックスがDまたはdの場合はdouble型です。例えば、1d、1.5d、1e10d、123.456Dなどのリテラルは、すべてdouble型です。
  • 実数リテラルのサフィックスがMまたはmの場合はdecimal型です。例えば、1m、1.5m、1e10m、123.456Mなどのリテラルは、すべてdecimal型です。

次回予告

 実は本稿を書き始める前は、varについてなど10行も書けば説明が終わるだろう、残りのページをどうやって埋めようか……と真剣に考えていた。しかし、いざフタを開けてみると、varが使用できる場面と使用できない場面の違いや、性質の詳しい説明など、いくらでも書くことはあった。結果として、ページを埋めるどころか、ページをあふれさせてしまった次第である。

 よって、この話題はまだ途上である。次回は、オブジェクト初期化子、匿名型、自動実装プロパティなどを取り上げる予定である。特に匿名型はvarについての説明と縁が深い。“名無し”の型は、型を明示した変数宣言ができないので、varを使うしかないのである。

 請うご期待。

 

 INDEX
  C# 3.0入門
  第3回 varによる変数宣言とコレクション初期化子
    1.暗黙的に型指定されるローカル変数/Variant型の悪夢/暗黙的に型を明示する
    2.なぜvarを使うのか?/varが使用できない場面
    3.varが使用できる場面/varとローカル配列/var配列と型の推測/var配列とnull
  4.コレクション初期化子/Dictionaryクラスとコレクション初期化子/コラム
 
インデックス・ページヘ  「C# 3.0入門」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間