web-dev-qa-db-ja.com

ラムダ式を受け入れるメソッドを書く

次のシグネチャを持つメソッドがあります。

 void MyMethod(Delegate d){};
 void MyMethod(Expression exp){};
 void MyMethod(object obj){};

ただし、これはコンパイルに失敗します。

MyMethod((int a) => a)

次のエラーが発生します:

"Cannot convert lambda expression to type 'object' because it is not a delegate type"

なぜこれが機能しないのですか?

編集:私はこれが機能することを知っています。この場合、コンパイラはラムダ式をデルゲートにコンパイルすると思います。

void MyMethod(Func<int, int> d){};

敬具、

25

タイプSystem.Delegateは「Delegate」ではないためです。それは単なる基本クラスです。正しい署名を持つデリゲートタイプを使用する必要があります。メソッドを次のように定義します。

void MyMethod(Func<int, int> objFunc)

編集:

MyMethod(object)は、ラムダ式自体に型がないため機能しませんが、型は割り当てられた場所の型から推測されます。したがって、オブジェクトも機能しません。正しい署名を持つデリゲートタイプを使用する必要があります。

20
void MyMethod(Action<int> lambdaHereLol)
{
    lambdaHereLol(2);
}

使用中で:

var hurrDurr = 5;
MyMethod(x => Console.Write(x * hurrDurr));

C#は静的に型付けされた言語です。コンパイラは、処理するすべてのもののタイプを知る必要があります。ラムダを特定するのは少し難しく、コンパイラーがそれを理解できない場合があります。上記の私の例では、MyMethodがオブジェクトを取得した場合、コンパイラはxintであることを理解できませんでした(私の例は単純ですが、多くないことを示すものは何もありませんより複雑で判断が難しい)。したがって、ラムダを受け取るメソッドをより明確に定義する必要があります。

14
Will

_(int a) => a_のようなラムダは、intを取り、intを返すすべてのデリゲートに適合します。 _Func<int,int>_はほんの一例であり、delegate int Foo(int x);を使用して自分で簡単に宣言できます。実際、このラムダ式は、ラムダ(int)の結果が暗黙的にdoubleに変換可能であるため、aを取りdoubleを返すデリゲートにも適合します。

ラムダが適合するすべてのデリゲート型に割り当て可能であるために、ラムダ自体は本質的に型を持っていません。代わりに、可能な限り、使用しているデリゲートのタイプを使用します。 (もちろん、_(int a) => a_を_Func<byte, byte>_に割り当てることはできません。)

_Func<int, int>_と私が定義したFooデリゲートの両方はもちろんDelegateに変換できますが、ラムダは実際の署名が何であるかが不明であるため、Delegateに直接変換することはできません。 Delegate d = (int a) => aの後、dFoo、または_Func<int, int>_、さらには_Func<int, double>_になりますか?すべてが有効な可能性であり、コンパイラはあなたが何を意図したのかわかりません。それは最良の推測をすることができますが、C#はその種の推測を行う種類の言語ではありません。これが、var = (int a) => aのようなことができない理由でもあります。

コンパイラがDelegate d = (int a) => a;に対して表示するエラーメッセージは非常に不明確だと思います。

デリゲート型ではないため、ラムダ式を型 'System.Delegate'に変換できません

直感的には、Delegateはデリゲート型だと思うでしょうが、それは物事の仕組みではありません。 :)

3
Joren

これを試して:

void MyMethod(Action<int> func) { }

メソッドへのパラメーターとして、強く型付けされたデリゲートが必要です。他の呼び出しが失敗する理由は、ラムダ式が必ずしもすべての場合にデリゲートであるとは限らないため、C#コンパイラではObjectを期待するメソッドにラムダ式を渡すことができないためです。これと同じルールが、ラムダ式をDelegateとして渡す場合にも適用されます。

上に示したような関数にラムダを渡すと、コンパイルでは、ラムダ式を特定のデリゲート型に変換する必要があると安全に想定できます。

2
Andrew Hare

デリゲートオブジェクトをDelegate型のパラメーターとして渡すときに、デリゲートオブジェクトをDelegateに明示的にキャストする必要があるのは、単にコンパイラーの性質です。実際、ラムダ式は、この場合、デリゲートに暗黙的に変換できないという点で、事態をさらに複雑にします。

必要なのは、ダブルキャストです。

MyMethod((Delegate)(Func<int, int>)((int a) => a));

もちろん、これはメソッドシグネチャに対応します。

void MyMethod(Delegate d);

状況によっては、DelegateではなくFunc<int>タイプのパラメーターを定義することをお勧めします(ただし、正直に言うと不必要な複雑さが追加されるため、オーバーロードを追加することを躊躇します)。

0
Noldorin

これが失敗する理由は、「object del =(int a)=> a」または「vardel =(int a)=> a」のような式が失敗するのと同じ理由です。引数の型を明示的に指定しているため、コンパイラはラムダ式の型を理解できると思うかもしれませんが、式がintを取り、intを返すことを知っていても、変換できるデリゲート型は多数あります。に。 Funcデリゲート型は、このようなジェネリック関数に最もよく使用される型ですが、これは単なる慣例であり、コンパイラーは何も認識していません。

通常のキャスト構文(Func)((int a)=> a)を使用するか、デリゲートを使用して、コンパイラにデリゲートオーバーロードを選択させるために、ラムダ式を具象デリゲート型にキャストする必要があります。コンストラクター構文newFunc((int a)=> a)。

また、受け入れる引数の数に応じて別の方法で何かを呼び出す必要がない限り、通常は型指定されていないDelegateクラスを使用したくありません。ほとんどの場合、コールバックなどの機能またはアクションを受け入れることをお勧めします。

0
SoftMemes