43

私はこの拡張メソッド(コンパイル)を作成しました:

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):この質問は答えられたと思いますが、過負荷の解決と型の制約に関する別の質問がありました。私がここに置いたこの質問:タイプ制約がメソッドシグネチャの一部ではないのはなぜですか?

4

2 に答える 2

93

まず、あなたは必要ありません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>議論を必要とします。これにより、型推論エンジンは両方の汎用型を判別できます。これらは両方とも、メソッドに提供された引数のみに基づいて使用できるためです。

于 2012-02-25T02:09:29.570 に答える
21

dlevの答えは大丈夫です。もう少し情報を追加したいと思いました。

具体的には、ジェネリックを使用して、にある種の共分散を実装しようとしていることに注意してくださいIEnumerable<T>C#4以降でIEnumerable<T>は、すでに共変です。

2番目の例はこれを示しています。あなたが持っている場合

List<List<int>> lists = whatever;
foreach(int x in lists.Flatten()) { ... }

次に、型推論は、に変換可能であり、に変換可能であり、したがって、共分散のために、に変換List<List<int>>可能であると推論します。それは型推論に何かを続けることを与えます。Tはintであり、すべてが良好であると推測できます。IE<List<int>>List<int>IE<int>IE<List<int>>IE<IE<int>>

これはC#3では機能しません。共分散のない世界では人生は少し難しいですが、Cast<T>拡張方法を慎重に使用することでうまくいくことができます。

于 2012-02-25T05:17:53.577 に答える