为什么用Invoke方法编译可以,直接return Func<int,int>编译不行?
Why is compilation OK, when I use Invoke method, and not OK when I return Func<int,int> directly?
我不明白这个案例:
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
的方法编译可以,直接return csharp Func<int,int>
的编译就不行?
在第二种情况下,f
是Func<int, int>
类型的,但是方法据说是return一个test
。这些是不相关的(委托)类型,它们不能相互转换,因此会发生编译器错误。您可以转到语言规范的 section,然后搜索“delegate”。您会发现没有提及具有相同签名的委托之间的转换。
然而,在第一种情况下,f.Invoke
是一个 方法组表达式 ,它实际上没有类型。 C# 编译器会根据上下文将方法组表达式转换为特定的委托类型,通过 method group conversion.
(引用第 5 个项目符号 here,强调我的)
An expression is classified as one of the following:
...
A method group, which is a set of overloaded methods resulting from a member lookup. [...] A method group is permitted in an invocation_expression, a delegate_creation_expression and as the left hand side of an is
operator, and can be implicitly converted to a compatible delegate type.
在这种情况下,它被转换为test
委托类型。
换句话说,return f
不起作用,因为 f
已经有类型,但 f.Invoke
还没有类型。
这里的问题是类型兼容性:
以下是来自 MSDN 资源的 Func 委托的定义:
public delegate TResult Func<in T, out TResult>(T arg);
如果你发现上面提到的 Func 和你定义的 Delegate 之间没有直接关系:
public delegate int test(int i);
Why 1st snippet compiles:
public test Success()
{
Func<int, int> f = x => x;
return f.Invoke; // <- code successfully compiled
}
Delegates通过signature进行比较,signature是输入参数和Output结果,最终一个Delegate是一个Function指针,两个函数只能通过签名进行比较。在运行时,通过 Func 调用的方法被分配给 Test
委托,因为 Signature 是相同的,所以它可以无缝地工作。这是一个函数指针赋值,其中 Test
委托现在将调用 Func 委托指向的方法
Why 2nd Snippet fails to compile
Func 和测试委托之间没有类型/赋值兼容性,Func 不能作为类型系统规则的一部分填写。即使它的结果可以像第一种情况那样分配并填写 test delegate
。
要了解此行为,您需要了解两件事。
- 所有委托都派生自
System.Delegate
,但不同的委托具有不同的类型,因此不能相互赋值。
- C# 语言provides special handling for assigning a method or lambda to a delegate。
因为不同的委托有不同的类型,这意味着您不能将一种类型的委托分配给另一种类型的委托。
例如,给定:
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.
上面的第一行编译成功,因为它使用特殊处理将 lambda 或方法分配给委托。
事实上,这一行被编译器有效地重写为:
test1 a = new test1(Console.WriteLine);
上面的第二行没有编译,因为它试图将一种类型的实例分配给另一种不兼容的类型。
就类型而言,test1
和 test2
之间没有兼容的分配,因为它们是不同的类型。
如果有助于思考,请考虑这个 class 层次结构:
class Base
{
}
class Test1 : Base
{
}
class Test2 : Base
{
}
以下代码将无法编译,即使 Test1
和 Test2
派生自相同的基 class:
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
}
我不明白这个案例:
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
的方法编译可以,直接return csharp Func<int,int>
的编译就不行?
在第二种情况下,f
是Func<int, int>
类型的,但是方法据说是return一个test
。这些是不相关的(委托)类型,它们不能相互转换,因此会发生编译器错误。您可以转到语言规范的 section,然后搜索“delegate”。您会发现没有提及具有相同签名的委托之间的转换。
然而,在第一种情况下,f.Invoke
是一个 方法组表达式 ,它实际上没有类型。 C# 编译器会根据上下文将方法组表达式转换为特定的委托类型,通过 method group conversion.
(引用第 5 个项目符号 here,强调我的)
An expression is classified as one of the following:
...
A method group, which is a set of overloaded methods resulting from a member lookup. [...] A method group is permitted in an invocation_expression, a delegate_creation_expression and as the left hand side of an
is
operator, and can be implicitly converted to a compatible delegate type.
在这种情况下,它被转换为test
委托类型。
换句话说,return f
不起作用,因为 f
已经有类型,但 f.Invoke
还没有类型。
这里的问题是类型兼容性:
以下是来自 MSDN 资源的 Func 委托的定义:
public delegate TResult Func<in T, out TResult>(T arg);
如果你发现上面提到的 Func 和你定义的 Delegate 之间没有直接关系:
public delegate int test(int i);
Why 1st snippet compiles:
public test Success()
{
Func<int, int> f = x => x;
return f.Invoke; // <- code successfully compiled
}
Delegates通过signature进行比较,signature是输入参数和Output结果,最终一个Delegate是一个Function指针,两个函数只能通过签名进行比较。在运行时,通过 Func 调用的方法被分配给 Test
委托,因为 Signature 是相同的,所以它可以无缝地工作。这是一个函数指针赋值,其中 Test
委托现在将调用 Func 委托指向的方法
Why 2nd Snippet fails to compile
Func 和测试委托之间没有类型/赋值兼容性,Func 不能作为类型系统规则的一部分填写。即使它的结果可以像第一种情况那样分配并填写 test delegate
。
要了解此行为,您需要了解两件事。
- 所有委托都派生自
System.Delegate
,但不同的委托具有不同的类型,因此不能相互赋值。 - C# 语言provides special handling for assigning a method or lambda to a delegate。
因为不同的委托有不同的类型,这意味着您不能将一种类型的委托分配给另一种类型的委托。
例如,给定:
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.
上面的第一行编译成功,因为它使用特殊处理将 lambda 或方法分配给委托。
事实上,这一行被编译器有效地重写为:
test1 a = new test1(Console.WriteLine);
上面的第二行没有编译,因为它试图将一种类型的实例分配给另一种不兼容的类型。
就类型而言,test1
和 test2
之间没有兼容的分配,因为它们是不同的类型。
如果有助于思考,请考虑这个 class 层次结构:
class Base
{
}
class Test1 : Base
{
}
class Test2 : Base
{
}
以下代码将无法编译,即使 Test1
和 Test2
派生自相同的基 class:
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
}