编译器模糊调用错误 - 使用Func<>的匿名方法和方法组或行动

时间:2010-01-13 14:05:40

标签: c# delegates

我有一个场景,我想使用方法组语法而不是匿名方法(或lambda语法)来调用函数。

该函数有两个重载,一个采用Action,另一个采用Func<string>

我可以愉快地使用匿名方法(或lambda语法)调用两个重载,但如果我使用方法组语法,则会出现 Ambiguous invocation 的编译器错误。我可以通过明确转换为ActionFunc<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() { }
}

C#7.3更新

根据0xcde下面的评论2019年3月20日(我发布此问题九年后)!此代码编译为C#7.3,感谢improved overload candidates

4 个答案:

答案 0 :(得分:97)

答案 1 :(得分:35)

编辑:我想我已经得到了它。

正如zinglon所说,这是因为即使编译时应用程序失败,也存在从GetStringAction的隐式转换。这是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非常不同)消除了歧义。

ActionFunc<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)

FuncAction的重载类似(因为它们都是代理)

string Function() // Func<string>
{
}

void Function() // Action
{
}

如果您注意到,编译器不知道要调用哪一个,因为它们仅因返回类型而不同。