例えば
渡された配列なりListなりの中身を標準出力に全部出力するDebug用メソッドを用意したとして、とりあえずはこうなると思うんですよね
public static class DebugUtil
{
public static void ListDump<T>(IEnumerable<T> datas)
{
foreach (var data in datas)
{
Console.WriteLine(data);
}
}
}
引数をIEnumerable<T>
にしておけば
static void Main(string[] args)
{
var data = new[] {1, 2, 3, 4, 5};
var list = new List<string>();
list.Add("Hello");
list.Add("Nice");
list.Add("Too");
list.Add("Meet");
list.Add("You");
DebugUtil.ListDump(data);
DebugUtil.ListDump(list);
}
配列でもListでも対応できるって寸法です。
ここで辞めておけばよかったんですが、さらに欲をかいて可変長引数対応もしたいな、ってなるわけです。
ちょっと追加してこんな感じ
public static class DebugUtil
{
public static void ListDump<T>(params T[] datas)
{
ListDump((IEnumerable<T>)datas);//キャストすることで、もう一つのListDumpを呼び出し
}
public static void ListDump<T>(IEnumerable<T> datas)
{
foreach (var data in datas)
{
Console.WriteLine(data);
}
}
}
こうしておけば
DebugUtil.ListDump("ほげ","ふが");
みたいな感じ可変長引数も対応! 便利!
んなこたない
ところが、これには罠が。
static void Main(string[] args)
{
var data = new[] {1, 2, 3, 4, 5};
var list = new List<string>();
list.Add("Hello");
list.Add("Nice");
list.Add("Too");
list.Add("Meet");
list.Add("You");
DebugUtil.ListDump(data);//OK
DebugUtil.ListDump(list);//NG
DebugUtil.ListDump("ほげ","ふが");//OK
}
このDebugUtil.ListDump(list);
は
System.Collections.Generic.List`1[System.String]
と出力されてしまうようになってしまいました。
問題はオーバーロードされたメソッドの処理順
まぁ、当たり前っちゃぁ当たり前なのかもですが、params T[]
を引数に取るListDumpの方が先に処理されちゃったわけです。
すると、T=List<string>
という事になり、List<string>の配列
がdatasに入った形に。
ぐぬぬー。
というわけで、目的通りの処理にするためには
DebugUtil.ListDump((IEnumerable<string>)list);
のように、明示的にキャストしてあげる必要がある。というわけでした。
めでたし・めでたくもなし。
おまけ
Genericsを辞めて、
public static class DebugUtil
{
public static void ListDump(params object[] datas)
{
ListDump((IEnumerable) datas);
}
public static void ListDump(IEnumerable datas)
{
foreach (var data in datas)
{
Console.WriteLine(data);
}
}
}
こんな感じで作ると、また挙動が変わって、DebugUtil.ListDump(list);
はListDump(IEnumerable datas)
に処理されるので思った通りの挙動になったりします。
これはobject
へのキャストよりもIEnumrable
へのアップキャストの方が階層が浅い(って言っていいのかな?)からだと思われます。
おまけのおまけ
public static class DebugUtil
{
public static void ListDump(params object[] datas)
{
if (datas.Length == 0) return;
if (datas.Length == 1)
{
var data = datas[0] as ICollection;
if (data != null)
{
ListDump(data);
return;
}
Console.WriteLine(datas[0]);
return;
}
ListDump((IEnumerable) datas);
}
public static void ListDump(IEnumerable datas)
{
foreach (var data in datas)
{
ListDump(data);
}
}
}
さらに、なんとなく適当に作ってみましたが、こんな感じで再起で行ったり来たりすると、配列の配列の配列のList みたいな変態的な構造も中身が全部表示される…かも。(あまりテストしてないです)
にしたって、もうちょっとマシな方法があるはず・・・。