为什么在使用Invoke方法时编译正常,而直接返回Func <int,int>却不正常?


28

我不了解这种情况:

public delegate int test(int i);

public test Success()
{
    Func<int, int> f = x => x;
    return f.Invoke; // <- code successfully compiled 
}

public test Fail()
{
    Func<int, int> f = x => x;
    return f; // <- code doesn't compile
}

为什么在使用Invoke方法时编译正常,而csharp Func<int,int>直接返回时却正常?


您有一个代表,这意味着您正在得到某种事件。该Invoke防止跨线程异常,并允许多个进程访问该对象。
jdweng

请注意,即使您使用两个相同的代表,例如delegate void test1(int i);delegate void test2(int i);
Matthew Watson

Answers:


27

要了解此行为,您需要了解两件事。

  1. 所有代表都来自System.Delegate,但是不同的代表具有不同的类型,因此不能彼此分配。
  2. C#语言为将方法或lambda分配给委托人提供了特殊的处理方法

由于不同的委托具有不同的类型,这意味着您不能将一种类型的委托分配给另一种。

例如,给定:

delegate void test1(int i);
delegate void test2(int i);

然后:

test1 a = Console.WriteLine; // Using special delegate initialisation handling.
test2 b = a;                 // Using normal assignment, therefore does not compile.

上面的第一行编译OK,因为它使用特殊处理将lambda或方法分配给委托。

实际上,编译器可以有效地重写此行:

test1 a = new test1(Console.WriteLine);

上面的第二行未编译,因为它试图将一种类型的实例分配给另一种不兼容的类型。

就类型而言,在test1和之间没有兼容的分配,test2因为它们是不同的类型。

如果可以考虑一下,请考虑以下类层次结构:

class Base
{
}

class Test1 : Base
{
}

class Test2 : Base
{
}

下面的代码将无法编译,即使Test1Test2来自同一个基类派生:

Test1 test1 = new Test1();
Test2 test2 = test1; // Compile error.

这解释了为什么您不能将一种委托类型分配给另一种。那只是普通的C#语言。

但是,关键是要了解为什么允许您将方法或lambda分配给兼容的委托。如上所述,这是对委托的C#语言支持的一部分。

因此,最后回答您的问题:

使用时,Invoke()您将使用特殊的C#语言处理将方法调用分配给委托,以便将方法或lambda分配给委托,而不是尝试分配不兼容的类型-因此它可以编译。

完全清楚,在您的OP中编译的代码:

public test Success()
{
    Func<int, int> f = x => x;
    return f.Invoke; // <- code successfully compiled 
}

实际上在概念上转换为:

public test Success()
{
    Func<int, int> f = x => x;
    return new test(f.Invoke);
}

而失败的代码正在尝试在两种不兼容的类型之间进行分配:

public test Fail()
{
    Func<int, int> f = x => x;
    return f; // Attempting to assign one delegate type to another: Fails
}

6

在第二种情况下,fis是type Func<int, int>,但是据说该方法返回a test。这些是不相关的(代理)类型,它们彼此不可转换,因此会发生编译器错误。您可以转到语言规范的这一部分,并搜索“代表”。您不会发现具有相同签名的委托人之间的转换。

但是,在第一种情况下,f.Invoke方法组表达式,它实际上没有类型。C#编译器将通过方法组转换根据上下文将方法组表达式转换为特定的委托类型。

这里引用第5点,重点是我的)

表达式分类为以下之一:

  • ...

  • 方法组,是由成员查找导致的一组重载方法。[...]方法组可以在invocation_expression,delegation_creation_expression中并且作为is运算符的左侧使用,并且可以隐式转换为兼容的委托类型。

在这种情况下,它将转换为test委托类型。

换句话说,return f由于f已经具有类型而f.Invoke不起作用,因此不起作用。


2

这里发出的是类型兼容性:

以下是来自MSDN Sources 的Func委托的定义:

public delegate TResult Func<in T, out TResult>(T arg);

如果您看到上述功能与您定义的代表之间没有直接关系:

public delegate int test(int i);

为什么第一段代码会编译:

public test Success()
{
    Func<int, int> f = x => x;
    return f.Invoke; // <- code successfully compiled 
 }

代表使用签名(输入参数和输出结果)进行比较,最终代表是一个函数指针,并且只能通过签名比较两个函数。在运行时,将通过Func调用的方法分配给Test委托,因为Signature相同,因此可以无缝工作。这是一个函数指针分配,其中的Test委托现在将调用Func委托所指向的方法

为什么第二个片段无法编译

在Func和测试委托之间,没有类型/赋值兼容性,Func无法作为Type系统规则的一部分进行填写。即使可以test delegate像第一种情况一样分配和填写其结果。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.