Kokudoriing

技術系与太話ブログ

コレクション初期化子を独自型に実装する

さて、昨日デバッガを騙すという記事を書いたんですが、
独自型に対してコレクション初期化子を適用したりしてました。
そう、C#3.0から独自型にもコレクション初期化子は適用可能です。
素敵!

ただもちろん条件はあります。
1.IEnumerable を実装していること。
2.Add という名前で引数を1つ取るメソッドを実装していること。

1.の制約はC#コンパイラの良心なのでしょうか。
コレクション初期化子を適用できる=列挙可能=IEnumerableを実装すべきなのでこれは非常に自然ですね。

そして2.の条件。
これはダック・タイピングと言われるものです。
つまり、型やシグニチャを無視して「Addという名前の引数を1つとるメソッド」をコンパイラが探す訳です。
つまりどんな型の引数でもいいし、戻り値は何でもいいわけです。

というわけで独自型にコレクション初期化子を適用してみました。

using System;
using System.Collections;

namespace ConsoleApplication
{
	class CustomCollection : IEnumerable
	{
		public void Add(object obj)
		{
			;
		}

		public IEnumerator GetEnumerator()
		{
			throw new NotImplementedException();
		}
	}

	class Program
	{
		static void Main(string[] args)
		{
			var hoge = new CustomCollection()
			{
				"hoge", "piyo", "fuga"
			};
		}
	}
}

これを ildasm でILに逆アセしてみます。

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // コード サイズ       40 (0x28)
  .maxstack  2
  .locals init ([0] class ConsoleApplication.CustomCollection '<>g__initLocal0')
  IL_0000:  newobj     instance void ConsoleApplication.CustomCollection::.ctor()
  IL_0005:  stloc.0
  IL_0006:  ldloc.0
  IL_0007:  ldstr      "hoge"
  IL_000c:  callvirt   instance void ConsoleApplication.CustomCollection::Add(object)
  IL_0011:  ldloc.0
  IL_0012:  ldstr      "piyo"
  IL_0017:  callvirt   instance void ConsoleApplication.CustomCollection::Add(object)
  IL_001c:  ldloc.0
  IL_001d:  ldstr      "fuga"
  IL_0022:  callvirt   instance void ConsoleApplication.CustomCollection::Add(object)
  IL_0027:  ret
} // end of method Program::Main

Main の中だけですが、IL 読めなくともなんとなくは分かると思います。
つまり、コレクション初期化子は各々の要素を1つ1つ Add メソッドで追加してるだけです。

ちなみに ICollection, ICollection を実装し、ICollection::Add メソッドを使用しても、
IL は ConsoleApplication.CustomCollection::Add を実行しています。

ただやっぱり独自コレクションを作る機会はあまりないのでどう活かすかが難しいですね。


どうでもいいですがはてなダイアリー、IL のシンタックスハイライトサポートしてないんですかね。