为什么编译可以,当我使用 Invoke 方法时,当我直接返回 Func<int,int> 时不可以?

Evg*_*hin 29 c# lambda delegates

我不明白这个案例:

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
}
Run Code Online (Sandbox Code Playgroud)

为什么用Invokemethod编译可以,csharp Func<int,int>直接返回就不行?

Mat*_*son 28

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

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

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

例如,给定:

delegate void test1(int i);
delegate void test2(int i);
Run Code Online (Sandbox Code Playgroud)

然后:

test1 a = Console.WriteLine; // Using special delegate initialisation handling.
test2 b = a;                 // Using normal assignment, therefore does not compile.
Run Code Online (Sandbox Code Playgroud)

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

事实上,这一行被编译器有效地重写如下:

test1 a = new test1(Console.WriteLine);
Run Code Online (Sandbox Code Playgroud)

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

就类型而言,由于它们是不同的类型test1test2因此它们之间没有兼容的分配。

如果它有助于思考,请考虑这个类层次结构:

class Base
{
}

class Test1 : Base
{
}

class Test2 : Base
{
}
Run Code Online (Sandbox Code Playgroud)

下面的代码将不会编译,即使Test1Test2派生自相同的基类:

Test1 test1 = new Test1();
Test2 test2 = test1; // Compile error.
Run Code Online (Sandbox Code Playgroud)

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

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

所以最后回答你的问题:

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

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

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

实际上在概念上转换为类似:

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

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

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


Swe*_*per 7

在第二种情况下,f是 type Func<int, int>,但该方法被称为返回 a test。这些是不相关的(委托)类型,彼此不可转换,因此会发生编译器错误。您可以转到语言规范的这一部分,然后搜索“委托”。您将发现没有提及具有相同签名的委托之间的转换。

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

这里引用第 5 项,强调我的)

表达式被归类为以下之一:

  • ...

  • 方法组,它是一组由成员查找产生的重载方法。[...] 在 invocation_expression、delegate_creation_expression 中允许方法组作为is运算符的左侧,并且可以隐式转换为兼容的委托类型。

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

换句话说,return f不起作用,因为f已经有一个类型,但f.Invoke还没有一个类型。