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
要了解这种行为,您需要了解两件事。
System.Delegate,但不同的委托具有不同的类型,因此不能相互分配。因为不同的委托有不同的类型,这意味着您不能将一种类型的委托分配给另一种类型。
例如,给定:
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)
上面的第二行无法编译,因为它试图将一种类型的实例分配给另一种不兼容的类型。
就类型而言,由于它们是不同的类型test1,test2因此它们之间没有兼容的分配。
如果它有助于思考,请考虑这个类层次结构:
class Base
{
}
class Test1 : Base
{
}
class Test2 : Base
{
}
Run Code Online (Sandbox Code Playgroud)
下面的代码将不会编译,即使Test1并Test2派生自相同的基类:
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)
在第二种情况下,f是 type Func<int, int>,但该方法被称为返回 a test。这些是不相关的(委托)类型,彼此不可转换,因此会发生编译器错误。您可以转到语言规范的这一部分,然后搜索“委托”。您将发现没有提及具有相同签名的委托之间的转换。
然而,在第一种情况下,f.Invoke是一个方法组 expression,它实际上没有类型。C# 编译器会根据上下文,通过方法组转换将方法组表达式转换为特定的委托类型。
(这里引用第 5 项,强调我的)
表达式被归类为以下之一:
...
方法组,它是一组由成员查找产生的重载方法。[...] 在 invocation_expression、delegate_creation_expression 中允许方法组作为
is运算符的左侧,并且可以隐式转换为兼容的委托类型。
在这种情况下,它被转换为test委托类型。
换句话说,return f不起作用,因为f已经有一个类型,但f.Invoke还没有一个类型。