我有一个场景,我想使用方法组语法而不是匿名方法(或lambda语法)来调用函数。
该函数有两个重载,一个采用Action
,另一个采用Func<string>
。
我可以愉快地使用匿名方法(或lambda语法)调用两个重载,但如果我使用方法组语法,则会出现 Ambiguous invocation 的编译器错误。我可以通过明确转换为Action
或Func<string>
来解决此问题,但不要认为这是必要的。
任何人都可以解释为什么需要明确的演员表。
下面的代码示例。
class Program
{
static void Main(string[] args)
{
ClassWithSimpleMethods classWithSimpleMethods = new ClassWithSimpleMethods();
ClassWithDelegateMethods classWithDelegateMethods = new ClassWithDelegateMethods();
// These both compile (lambda syntax)
classWithDelegateMethods.Method(() => classWithSimpleMethods.GetString());
classWithDelegateMethods.Method(() => classWithSimpleMethods.DoNothing());
// These also compile (method group with explicit cast)
classWithDelegateMethods.Method((Func<string>)classWithSimpleMethods.GetString);
classWithDelegateMethods.Method((Action)classWithSimpleMethods.DoNothing);
// These both error with "Ambiguous invocation" (method group)
classWithDelegateMethods.Method(classWithSimpleMethods.GetString);
classWithDelegateMethods.Method(classWithSimpleMethods.DoNothing);
}
}
class ClassWithDelegateMethods
{
public void Method(Func<string> func) { /* do something */ }
public void Method(Action action) { /* do something */ }
}
class ClassWithSimpleMethods
{
public string GetString() { return ""; }
public void DoNothing() { }
}
根据0xcde下面的评论2019年3月20日(我发布此问题九年后)!此代码编译为C#7.3,感谢improved overload candidates。
答案 0 :(得分:97)
答案 1 :(得分:35)
正如zinglon所说,这是因为即使编译时应用程序失败,也存在从GetString
到Action
的隐式转换。这是6.6节的介绍,重点是(我的):
存在隐式转换(第6.1节) 从方法组(第7.1节)到a 兼容的委托类型。给出一个 委托类型D和表达式E. 被归类为方法组, E存在隐式转换 如果E包含至少一种方法,则为D. 适用于其正常形式 (§7.4.3.1)参数列表 通过使用参数构造 如上所述,D 的类型和修饰符 在下面。
现在,我对第一句话感到困惑 - 它谈到了转换为兼容的委托类型。 Action
不是GetString
方法组中任何方法的兼容委托,但GetString()
方法 适用于其正常形式的参数列表使用D的参数类型和修饰符。注意,这个没有谈论D的返回类型。这就是为什么它变得混乱...因为它只会检查{的委托兼容性{1}}当应用转换时,不检查其存在。
我认为暂时超出方程式是有益的,并了解转换的存在与其适用性之间的差异如何显现。这是一个简短而完整的例子:
GetString()
using System;
class Program
{
static void ActionMethod(Action action) {}
static void IntMethod(int x) {}
static string GetString() { return ""; }
static void Main(string[] args)
{
IntMethod(GetString);
ActionMethod(GetString);
}
}
中的方法调用表达式都没有编译,但错误消息不同。这是Main
的一个:
Test.cs(12,9):错误CS1502:最好的 重载方法匹配 'Program.IntMethod(int)'有一些 无效的参数
换句话说,规范的7.4.3.1节找不到任何适用的函数成员。
现在这是IntMethod(GetString)
的错误:
Test.cs(13,22):错误CS0407:'string Program.GetString()'有错误 返回类型
这次它确定了要调用的方法 - 但是它无法执行所需的转换。不幸的是,我无法找到最终检查所执行的规范 - 它看起来像可能在7.5.5.1中,但我看不到确切的位置。
旧的答案被删除了,除了这一点 - 因为我希望埃里克能够阐明这个问题的“原因”......
仍然在寻找...同时,如果我们三次说“Eric Lippert”,你认为我们会去拜访(从而得到答案)吗?
答案 2 :(得分:1)
在Func<string>
中使用Action<string>
和Action
(显然与Func<string>
和ClassWithDelegateMethods
非常不同)消除了歧义。
Action
和Func<int>
之间也存在歧义。
我也有这个含糊不清的错误:
class Program
{
static void Main(string[] args)
{
ClassWithSimpleMethods classWithSimpleMethods = new ClassWithSimpleMethods();
ClassWithDelegateMethods classWithDelegateMethods = new ClassWithDelegateMethods();
classWithDelegateMethods.Method(classWithSimpleMethods.GetOne);
}
}
class ClassWithDelegateMethods
{
public void Method(Func<int> func) { /* do something */ }
public void Method(Func<string> func) { /* do something */ }
}
class ClassWithSimpleMethods
{
public string GetString() { return ""; }
public int GetOne() { return 1; }
}
进一步的实验表明,当通过self传递方法组时,在确定要使用哪个重载时,将完全忽略返回类型。
class Program
{
static void Main(string[] args)
{
ClassWithSimpleMethods classWithSimpleMethods = new ClassWithSimpleMethods();
ClassWithDelegateMethods classWithDelegateMethods = new ClassWithDelegateMethods();
//The call is ambiguous between the following methods or properties:
//'test.ClassWithDelegateMethods.Method(System.Func<int,int>)'
//and 'test.ClassWithDelegateMethods.Method(test.ClassWithDelegateMethods.aDelegate)'
classWithDelegateMethods.Method(classWithSimpleMethods.GetX);
}
}
class ClassWithDelegateMethods
{
public delegate string aDelegate(int x);
public void Method(Func<int> func) { /* do something */ }
public void Method(Func<string> func) { /* do something */ }
public void Method(Func<int, int> func) { /* do something */ }
public void Method(Func<string, string> func) { /* do something */ }
public void Method(aDelegate ad) { }
}
class ClassWithSimpleMethods
{
public string GetString() { return ""; }
public int GetOne() { return 1; }
public string GetX(int x) { return x.ToString(); }
}
答案 3 :(得分:0)
Func
和Action
的重载类似(因为它们都是代理)
string Function() // Func<string>
{
}
void Function() // Action
{
}
如果您注意到,编译器不知道要调用哪一个,因为它们仅因返回类型而不同。