【问题标题】:Write a method which accepts a lambda expression编写一个接受 lambda 表达式的方法
【发布时间】:2009-10-20 12:13:22
【问题描述】:

我有一个带有以下签名的方法:

 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"

为什么这不起作用?

编辑:我知道这行得通。在这种情况下,我认为编译器会将 lambda 表达式编译为一个 delgate。

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

亲切的问候,

【问题讨论】:

  • 仅供参考,每当编译失败时,请阅读(并发布)错误消息。
  • @SharePoint 新手:请参阅我更新的帖子。现在应该可以解决您的错误了。

标签: c# lambda


【解决方案1】:

因为 System.Delegate 类型不是“委托”。这只是基类。您必须使用具有正确签名的委托类型。如下定义您的方法:

void MyMethod(Func<int, int> objFunc)

编辑:

MyMethod(object) 不起作用,因为 lambda 表达式本身没有类型,但类型是从分配给它的位置的类型推断出来的。所以对象也不起作用。您必须使用具有正确签名的委托类型。

【讨论】:

  • 我知道这行得通,我想知道为什么其他签名不起作用。
  • 然后阅读我写的内容。 System.Delegate 不是委托,无法将 lambda 表达式转换为 System.Delegate。事实上,lambda 表达式本身没有类型,它们从分配给它们的变量的类型中获取类型,因此 object 也不起作用。
  • 但我自己指定类型。它没有被隐式推断,“(int a)=> a;”。您能否解释一下,lambda 表达式本身没有类型?
  • 查看 C# 语言规范 3.0 版,第 6.5 节匿名函数转换。
【解决方案2】:
void MyMethod(Action<int> lambdaHereLol)
{
    lambdaHereLol(2);
}

使用中:

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

C# 是一种静态类型语言。编译器需要知道它处理的所有内容的类型。 Lambda 有点难以确定,有时编译器无法弄清楚。在我上面的示例中,如果 MyMethod 获取一个对象,编译器无法确定 xint (我的示例很简单,但没有什么可以说明它不能更复杂和更难确定)。所以我必须更明确地定义采用我的 lambda 的方法。

【讨论】:

  • 其他人总是把“cloture”和“closure”混为一谈吗?
  • “C# 是一种静态类型语言。编译器需要知道它所处理的所有内容的类型。”。这是真的,但其余的不符合。有很多静态类型语言可以为您推断类型签名。 F# 就是一个例子。
  • @rgrinberg:除了编译器这样做,并且在编译时使用推理来确定类型。所以我不确定你想要做出的区分。
  • 我搜索了 10 分钟才找到这个。谢谢!
【解决方案3】:

(int a) =&gt; a 这样的 lambda 表达式将适合任何接受 int 并返回 int 的委托。 Func&lt;int,int&gt; 只是一个例子,你可以很容易地用delegate int Foo(int x); 自己声明一个。事实上,这个 lambda 表达式甚至适合接受 int 并返回 double 的委托,因为 lambda (a) 的结果可以隐式转换为 double

为了使 lambda 可以分配给它适合的所有委托类型,lambda 本身并没有固有的类型。相反,只要可能,它就会采用您正在使用它的委托的类型。 ((int a) =&gt; a 当然不能分配给Func&lt;byte, byte&gt;。)

虽然我定义的 Func&lt;int, int&gt;Foo 委托当然都可以转换为 Delegate,但 lambda 不能直接转换为 Delegate,因为不清楚它的实际签名会是什么。在Delegate d = (int a) =&gt; a 之后,d 会是Foo,还是Func&lt;int, int&gt;,甚至是Func&lt;int, double&gt;?所有都是有效的可能性,编译器不知道你的意图。它可以做出最好的猜测,但 C# 不是那种做这种猜测的语言。这也是你不能做var = (int a) =&gt; a之类的事情的原因。

我确实认为编译器为Delegate d = (int a) =&gt; a; 提供的错误消息非常不清楚:

无法将 lambda 表达式转换为类型“System.Delegate”,因为它不是委托类型

直觉上你会认为Delegate 是一个委托类型,但事实并非如此。 :)

【讨论】:

    【解决方案4】:

    试试这个:

    void MyMethod(Action<int> func) { }
    

    你需要一个强类型的委托作为方法的参数。其他调用失败的原因是因为 C# 编译器不允许您将 lambda 表达式传递给需要 Object 的方法,因为 lambda 表达式不一定在所有情况下都是委托。同样的规则也适用于将 lambda 表达式作为 Delegate 传递。

    当您将 lambda 传递给我上面展示的函数时,编译器可以安全地假设您希望将 lambda 表达式转换为特定的委托类型并执行此操作。

    【讨论】:

    • 我知道这行得通,我想知道为什么其他签名不起作用。
    【解决方案5】:

    这只是编译器的本质,当您将委托对象作为Delegate 类型的参数传递时,您需要将其显式转换为Delegate。事实上,lambda 表达式使事情变得更加复杂,因为在这种情况下它们不能隐式转换为委托。

    您需要的是双重转换,例如:

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

    当然对应方法签名:

    void MyMethod(Delegate d);
    

    根据您的情况,您可能希望定义 Func&lt;int&gt; 而不是 Delegate 类型的参数(尽管我会犹豫是否添加重载,因为它增加了不必要的复杂性)。

    【讨论】:

    • 是的,因为正如我所写,System.Delegate 不是代表。它只是代表的基本类型。
    • 糟糕,你是对的。我错过了转换为需要首先发生的委托类型。这种方法可以深入了解 C# 编译器如何将 lambda 表达式隐式转换为委托,以及它如何将其作为参数传递。您在此处看到的是“手动”执行所有操作。
    【解决方案6】:

    失败的原因与“object del = (int a) => a”甚至“var del = (int a) => a”这样的表达式失败的原因相同。您可能认为编译器可以计算出您的 lambda 表达式的类型,因为您明确给出了参数的类型,但即使知道表达式接受一个 int 并返回一个 int,也有许多委托类型可以转换到。 Func 委托类型是最常用于诸如此类的泛型函数的类型,但这只是一种约定,编译器并没有意识到这一点。

    您需要做的是将 lambda 表达式转换为具体的委托类型,以便让编译器选择委托重载,或者使用正常的转换语法 (Func)((int a) => a),或者使用委托构造函数语法 new Func((int a) => a).

    此外,您通常不希望使用无类型的 Delegate 类,除非您需要根据它接受的参数数量来调用不同的东西。对于回调之类的事情,接受 Func 或 Action 几乎总是更好。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-01-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-14
      • 1970-01-01
      • 1970-01-01
      • 2012-10-24
      相关资源
      最近更新 更多