私はこの拡張メソッド(コンパイルする)を書きました:
public static IEnumerable<J> Flatten<T, J>(this IEnumerable<T> @this)
where T : IEnumerable<J>
{
foreach (T t in @this)
foreach (J j in t)
yield return j;
}
以下のコードは、コンパイル時エラー(適切なメソッドが見つからない)を引き起こします、理由:
IEnumerable<IEnumerable<int>> foo = new int[2][];
var bar = foo.Flatten();
以下のように拡張機能を実装すると、コンパイル時エラーは発生しません。
public static IEnumerable<J> Flatten<J>(this IEnumerable<IEnumerable<J>> @this)
{
foreach (IEnumerable<J> js in @this)
foreach (J j in js)
yield return j;
}
Edit(2):この質問は答えられたと思いますが、過負荷の解決と型の制約に関して別の質問がありました。私がここに置いたこの質問: タイプ制約がメソッドシグネチャの一部ではないのはなぜですか?
まず、Flatten()
は必要ありません。そのメソッドはすでに存在し、SelectMany()
と呼ばれます。次のように使用できます。
_IEnumerable<IEnumerable<int>> foo = new [] { new[] {1, 2}, new[] {3, 4} };
var bar = foo.SelectMany(x => x); // bar is {1, 2, 3, 4}
_
第二に、ジェネリック型の推論はメソッドへの引数に基づいてのみ機能し、メソッドに関連付けられたジェネリック制約ではないため、最初の試みは機能しません。 J
汎用パラメーターを直接使用する引数がないため、型推論エンジンはJ
がどうあるべきかを推測できず、したがって、メソッドが候補であるとは見なしません。
SelectMany()
がこれをどのように回避するかを見るのは啓発的です:追加の_Func<TSource, TResult>
_引数が必要です。これにより、型推論エンジンは両方の汎用型を判別できます。これらは両方とも、メソッドに提供された引数のみに基づいて使用できるためです。
dlevの答えは結構です。もう少し情報を追加すると思いました。
具体的には、ジェネリックを使用してIEnumerable<T>
に一種の共分散を実装しようとしていることに注意してください。 C#4以降では、IEnumerable<T>
はすでに共変です。
2番目の例はこれを示しています。あなたが持っている場合
List<List<int>> lists = whatever;
foreach(int x in lists.Flatten()) { ... }
次に、型推論により、List<List<int>>
はIE<List<int>>
に変換可能であり、List<int>
はIE<int>
に変換可能であり、したがって、共分散のため、IE<List<int>>
はIE<IE<int>>
に変換可能であると推論されます。それは型推論に何かを続けることを与えます。 Tはintであり、すべてが良好であると推測できます。
これはC#3では機能しません。共分散のない世界では、生活は少し難しくなりますが、Cast<T>
拡張メソッドを慎重に使用することで問題を解決できます。