【问题标题】:Anonymous function conversion匿名函数转换
【发布时间】:2019-01-29 05:45:59
【问题描述】:

C# 语言规范6.5 匿名函数转换 指出:

...

具体来说,匿名函数 F 与提供的委托类型 D 兼容:

...

如果 F 不包含 匿名函数签名,那么 D 可能有 零个或多个任意类型的参数,只要 D 的参数没有 out 参数修饰符。

但是,以下代码会产生错误。

using System;
namespace DelegateAnonymousFunctionExample
{
    public delegate void D(int i, int b);
    class Program
    {
        static void Main(string[] args)
        {
            // Valid
            D f1 = (int a, int b) =>
            {
                Console.WriteLine("Delegate invoked...");
            };
            f1(3, 4);

            // Error
            D f2 = () =>
            {
                Console.WriteLine("Delegate invoked...");
            };

            Console.ReadKey();
        }
   }
}

我在上面的代码中哪里出错了?

【问题讨论】:

标签: c# delegates anonymous-function


【解决方案1】:

f2 变量未收到有效的方法签名,您委托 D 正在等待 2 个参数。

【讨论】:

  • 您好,那么您如何解释规范?
  • 您能解释一下这如何满足问题中定义的规范的第二部分吗?
  • 这不能回答问题。 OP 的目的是询问如何看待规范
  • 直觉上我会说同样的话。但是根据规范,至少我对它的理解,它不应该产生错误。所以在我不知道的地方存在误解。
【解决方案2】:

您指出的 6.5 美元规格还说:

表达式没有类型,但可以隐式转换为 兼容的委托类型或表达式树类型

和 6.5.1 美元:

匿名函数到委托类型的转换会产生一个 引用匿名函数的委托实例

匿名方法可以用 lambda 表达式定义, 实际上,这些东西是不同的。

Lambda expression 没有类型,可以隐式转换为Expression,用于LINQ 查询表达式和Anonymous 方法定义。

因此,在您的错误情况下,lambda 表达式将被转换为不兼容的委托实例,它不接受任何参数。

引用您提供的规范说明

D f5 = delegate { Console.WriteLine("Delegate invoked..."); };

它之所以有效,是因为没有指向签名,它将自动编译为兼容的委托实例,该实例接受 (int, int)。

“如果 F 不包含匿名函数签名” 表示 签名缺失,但 lambda 的 '()' 表示 不接受任何参数的签名。正如@Johnny 的回答中提到的那样,没有签名就不能声明 lambda。关于委托示例:查看代码示例中的第 4 个示例 - 它是无效的,因为具有不兼容的签名,不接受任何参数,第 5 个示例没有签名并且它是有效的。

您也可以复制下面的示例并检查它的编译方式(只需先注释无效代码) https://sharplab.io/

public delegate void D(int i, int b);

public void Main(string[] args) 
{        
    //valid, lambda expression will be converted to compatible delegate instance
    D f1 = (int a, int b) => Console.WriteLine("Delegate invoked...");
    f1(3, 4);

    //INVALID, lambda expression will be converted to incompatible delegate instance
    D f2 = () => Console.WriteLine("Delegate invoked...");
    f2(3, 4);

    //valid, assigning delegate with compatible signature
    D f3 = delegate(int i, int j) { Console.WriteLine("Delegate invoked..."); };
    f3(3, 4);

    //INVALID, assigning delegate with incompatible signature
    D f4 = delegate() { Console.WriteLine("Delegate invoked..."); };
    f4(3, 4);

    //valid, it will be automatically compiled to compatible delegate instance which accepts (int, int)
    D f5 = delegate { Console.WriteLine("Delegate invoked..."); };
    f5(3, 4);
}

【讨论】:

  • 这很奇怪。在规范中,如果我没记错的话,匿名函数包括匿名方法表达式和 lambda 表达式。
  • Lambda表达式其实没有类型,可以隐式转换为表达式或者匿名方法,给我点时间,我差不多找到原因了……
  • 规范使用术语“匿名函数”,可以是匿名方法或 lambda 表达式。第 6.5 节匿名函数转换在第一句话中明确说明了这一点。所以使用 lambda 表达式不应该是错误的原因。但是,您的推理是“它之所以有效,是因为未指向签名,并且它将自动编译为接受(int,int)的兼容委托实例。”,imo,是答案的关键。从语法上讲,根本没有办法编写没有括号的 lambda 表达式,它的存在意味着一个签名。
  • @woldmar 所以实际上,在这种情况下排除了 lambda 表达式。我接受了你的回答。
  • @JohnSmithSr.,我认为只是“如果 F 不包含匿名函数签名”表示没有签名,但 lambda 的 '()' 表示签名不接受参数。正如约翰尼的回答中提到的那样,也不能在没有签名的情况下声明 lambda。关于代表示例:查看代码示例中的第 4 个示例 - 它是无效的,因为具有不兼容的签名,不接受任何参数,第 5 个示例没有签名并且它是有效的。
【解决方案3】:

委托是一种表示对具有特定参数列表和返回类型的方法的引用的类型

原则上一切都是从delegate开始的,上面写着特定参数列表返回类型

如果 F 不包含匿名函数签名,则 D 可能有 零个或多个任意类型的参数,只要 D 的参数没有 out 参数修饰符。

可以使用语法delegate parameter-list { statement-list } 声明匿名方法。在这种情况下,您可以省略 parameter-list 上面所说的内容是有效的。另一方面,如果您提供参数,则参数类型必须完全匹配。

public delegate void MyDelegate(int a);

MyDelegate d = delegate { }; //valid
MyDelegate d = delegate(int a) { }; //valid
MyDelegate d = delegate(int a, int b) { }; //invalid

public delegate void MyDelegateOut(int a, out int b);
MyDelegateOut d = delegate { }; //invalid

如果要使用 lambda 声明 delegate,省略效果无法归档,语法为 (input-parameters) => { statement-list em>}

【讨论】:

  • 谢谢,你的帖子很有帮助,尤其是最后一部分。
猜你喜欢
  • 2018-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多