私は次のサンプルコードを持っています(C#3.5の学習目的にのみ使用されます)。
IEnumerableとsort関数を受け付けるSort関数を呼び出しています。ラムダ式を使用してそれを呼び出すと(ケースA)、コンパイラは戻り型TResultを派生させることができますが、func SortIntを渡すと(ケースB)、コンパイラはエラーをスローします。
2番目のケースでコンパイラがなぜTResultを導出できないのか理解できません。私はまったく同じ情報を渡しているようです。それとも正確ではないですか?
助けてください !
int[] intArray = { 1, 3, 2, 5, 1 };
IEnumerable<int> intArray2 = Sort(intArray, x => SortInt(x)); // <= CASE A - OK !
IEnumerable<int> nextIntArray = Sort(intArray, SortInt); // <= CASE B - Compile Error: Cannot Infer Type !
public static IEnumerable<TResult> Sort<T, TResult>(IEnumerable<T> toBeSorted,
Func<IEnumerable<T>, IEnumerable<TResult>> sortFunc)
{
return sortFunc(toBeSorted);
}
public static IEnumerable<int> SortInt(IEnumerable<int> array)
{
return array.OrderByDescending(x => x);
}
2番目の例では、コンパイラがSortIntに対してオーバーロード解決を実行できないため、推論が失敗したように見えます。
これはより徹底的な説明として役に立つかもしれません:
型推論がメソッドグループの変換にはうまくいかないのは厄介だと思います。グループ内に適用可能なメソッドが1つしかないという一般的なケースでは、C#コンパイラをより賢くしたいと思います。ただし、複雑な状況では、さまざまな型が推論される原因となる、複数の適用可能なオーバーロードが発生する可能性があります。
オプション:
基本的に私は最初の2つの選択肢のどちらかに固執するでしょう、それらが両方ともそうであるように迷惑です。最初のオプションでは間接的なレベルが増えるため、パフォーマンスがわずかに低下しますが、通常はそれほど重要ではありません。
IEnumerable<int> intArray = Sort(intArray, x => SortInt(x)); // <= CASE A - OK !
このステートメントでは、x => SortInt(x)は次のようにデリゲート表現です。
delegate(IEnumerable<int> x){
return SortInt(x);
}
技術的には、ステートメントのどこかにSortIntへの参照を渡していません。
Func、IEnumerable>はデリゲート型です。つまり、関数へのポインタは必要ですが、関数は必要ありません。他にSortIntがメソッドです。
さらに深く掘り下げてみると、x => SortInt(x)の実際のコンパイル時表現は次のようになります。
private static IEnumerable<int> __XYZ(IEnumerable<int> x){
return SortInt(x);
}
private delegate IEnumerable<int> __XYZHANDLER(IEnumerable<int> x);
そしてあなたの1行目は
IEnumerable<int> intArray = Sort(intArray, new __XYZHANDLER(__XYZ) );
今あなたの2行目を見て、SortIntは何もない、そのメソッド名です、それは任意の型やインスタンスではありません。メソッドパラメータが何かを持てるのは、何らかの型のインスタンスだけです。 new Delegateはメソッドポインタのインスタンスであり、メソッドではありません。
x => SortInt(x)は上記で説明したすべての短縮形です。lamdaは通常、匿名デリゲートを書くより小さな形式です。コンパイラはx => SortInt(x)に対してSortIntを推論しません。
あなたのコードはそれが機能するのを妨げていたいくつかの問題を抱えていました。
まず第一に、あなたのタイプはすべて一致しませんでした。 int []をIEnumerableとして渡していましたが、.AsEnumerable()を呼び出さずにそれを実行することはできません。
第2に、TとTResultは、使い方は同じですが(int)、コンパイラと同じではありませんが、整数の配列を渡し、結果の種類が何であるかを言わずにIEnumerableを期待しています。それで、あなたはあなたのバージョンと一緒にTResultの型を渡す必要があるでしょう(Sort(intArray.AsEnumerable()、SortInt)のように)、しかしあなたはちょうど同じ型Tを注文されるのであなたはTResultを必要としません
だから、私はTResultを取り除き、タイプを修正しました:
void Main()
{
var intArray = new [] { 1, 3, 2, 5, 1 };
var ints = Sort(intArray.AsEnumerable(), x => SortInt(x));
var nextInts = Sort(intArray.AsEnumerable(), SortInt);
}
public static IEnumerable<T> Sort<T>(
IEnumerable<T> toBeSorted,
Func<IEnumerable<T>, IEnumerable<T>> sortFunc)
{
return sortFunc(toBeSorted);
}
public static IOrderedEnumerable<T> SortInt<T>(IEnumerable<T> array)
{
return array.OrderByDescending(x => x);
}
私は上記を好む、しかしこれは私があなたのサンプルに到達することができる最も近いものであり、それはうまくいく:
void Main() {
var intArray = new [] { 1, 3, 2, 5, 1 };
var ints = Sort(intArray.AsEnumerable(), x => SortInt(x));
var nextInts = Sort<int, int>(intArray.AsEnumerable(), SortInt);
}
public static IEnumerable<TResult> Sort<T, TResult>(
IEnumerable<T> toBeSorted,
Func<IEnumerable<T>, IEnumerable<TResult>> sortFunc)
{
return sortFunc(toBeSorted);
}
public static IEnumerable<int> SortInt(IEnumerable<int> array){
return array.OrderByDescending(x => x);
}
SortIntは次のようにもなります。
public static IOrderedEnumerable<T> SortInt<T>(IEnumerable<T> array){
return array.OrderByDescending(x => x);
}
Akashは、ラムダ式を使用したときに違いが生じる理由について説明しています。