编译器调用不明确的错误 - 匿名方法和方法组Func键<>或行动 [英] Compiler Ambiguous invocation error - anonymous method and method group with Func<> or Action

查看:97
本文介绍了编译器调用不明确的错误 - 匿名方法和方法组Func键<>或行动的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有我想要的使用方法组语法,而不是匿名方法(或lambda语法),用于调用一个函数的情况。

该函数有两个重载,一个接受一个动作,其他需要 Func键<串>

我可以高兴地调用使用匿名方法(或lambda语法)的两个重载,但得到的编译器错误的不明确调用的,如果我用的方法组语法。我可以明确的铸造解决方法,以动作 Func键<串> ,但不认为这应该是必要的。

任何人都可以解释为什么应该要求明确的类型转换。

低于code样本。

 类节目
{
    静态无效的主要(字串[] args)
    {
        ClassWithSimpleMethods classWithSimpleMethods =新ClassWithSimpleMethods();
        ClassWithDelegateMethods classWithDelegateMethods =新ClassWithDelegateMethods();        //这些都编译(lambda语法)
        classWithDelegateMethods.Method(()=> classWithSimpleMethods.GetString());
        classWithDelegateMethods.Method(()=> classWithSimpleMethods.DoNothing());        //这些还编译(有明确的转换方法组)
        classWithDelegateMethods.Method((Func键<串GT;)classWithSimpleMethods.GetString);
        classWithDelegateMethods.Method((动作)classWithSimpleMethods.DoNothing);        //这些都与暧昧调用错误(方法组)
        classWithDelegateMethods.Method(classWithSimpleMethods.GetString);
        classWithDelegateMethods.Method(classWithSimpleMethods.DoNothing);
    }
}类ClassWithDelegateMethods
{
    公共无效方法(Func键<串GT; FUNC){/ *做某事* /}
    公共无效方法(行动行动){/ *做某事* /}
}类ClassWithSimpleMethods
{
    公共字符串的GetString(){返回; }
    公共无效DoNothing(){}
}


解决方案

首先,请允许我只想说,乔恩的答案是正确的。这是规范的hairiest部分之一,乔恩那么好潜水进去第一头。

二,让我说,这行:

这是隐式转换从方法组存在一个兼容的委托类型

(强调)是深受误导和不幸。我会和的Mads有关获取此处被拆除的话兼容的演讲。

究其原因,这是误导,不幸的是,因为它看起来这是呼唤15.2节,委派的兼容性。第15.2节描述的方法和委托类型之间的相容性关系,但是这是方式组和委托类型可兑换的问题,这是不同的。

现在,我们已经得到了出路,我们可以通过该规范第6.6走,看看我们得到的。

要做到,我们必须首先确定哪个重载是重载的适用候选人的。如果所有的参数都是隐式转换为正式参数类型的候选人都是适用的。考虑你的计划这一简化版:

 类节目
{
    委托无效D1();
    代表串D2();
    静态字符串X(){返回null; }
    静态无效Y(D1 D1){}
    静态无效Y(D2 D2){}
    静态无效的主要()
    {
        Y(X);
    }
}

让我们通过它去一行一行。


  

这是隐式转换从方法组到兼容的委托类型存在。


我已经讨论过的单词兼容怎么又是不幸的在这里。继续。 Y上做重载决议(X)时,我们想知道,是否法团X转换为D1?它转换为D2?


  

给定一个委托类型D和一个
  被列为前pressionË
  方法组,隐式转换
  存在从E到d。如果E包含在
  至少一种方法,其适用[...]到
  参数列表通过使用构造
  参数类型和改性剂
  D,如下面说明。


到目前为止好。点¯x可能包含适用与D1或D2的参数列表的方法。


  

从方法E组到委托D型转换的编译时应用的描述如下。


这行真的没有说什么有意思的。


  

注意给E到D的隐式转换的存在并不能保证转换的编译时应用程序没有错误成功。


这行是迷人的。这意味着,有其存在的隐式转换,但受到被变成错误!这是C#一个奇怪的规则。离题了一下,这里有一个例子:

 无效Q(前pression<&Func键LT;串GT;> F){}
串M(INT X){...}
...
INT Y = 123;
Q(()=> M +(Y ++));

这是增值业务处于前pression树非法的。然而,在lambda仍转换应用于前pression乔木型,即使AD转换是否使用过,这是一个错误!这里的原则是,我们可能要改变什么可以在一个前pression树后去规则;改变这些规则不应该改变的类型系统的规则的。我们要强迫你做你的程序明确的现在的,所以,当我们改变了前pression树木的规则在未来,使他们更好的我们不破介绍在重载的变化

无论如何,这是这种奇异规则的另一个例子。 A转换可以存在重载决议的目的,但要真正使用错误。但事实上,这是不完全的情况下,我们都在这里。

上移动


  

一个单一的方法M被选中对应的形式E(A)的方法调用[...]参数列表A是前pressions的列表,每个归类为一个变量[...]在D的形式参数列表中的相应参数。


确定。所以,我们做超负荷X上的分辨率相对于D1。 D1的形式参数列表是空的,所以我们做超负荷X()和喜悦的分辨率,我们发现了一种方法,作品串X()。同样,D2的形式参数列表是空的。此外,我们发现,串X()是一个太在这里工作的方法。

这里的原则是,的确定方法组可兑换需要使用重载的方法组中选择的方法的,和重载不考虑返回类型


  

如果该算法[...]产生一个错误,则发生编译时错误。否则该算法产生具有相同数目的参数为D单个最佳方法M和转​​换被认为是存在的。


有只有一个在方法团X的方法,所以它必须是最好的。我们已经成功地证明,一个转换的存在从X到D1,并从X到D2。

现在,是该行有关?


  

选择的方法m必须与委托类型D,或以其他方式兼容,发生编译时错误。


其实,不,不是在这个程序。据我们从来没有为激活这一行。因为,请记住,我们在这里所做的是试图做Y(X)重载决议。我们有两个候选人Y(D1)和Y(D2)。两者都是适用的。这是的更好的? 无处在规范中做我们描述这两个可能的转换之间betterness

现在,人们当然可以认为,一个有效的转换比一个产生错误要好。那么这将有效地说,在这种情况下,重载解析会考虑返回类型,这是我们要避免的东西。接下来的问题是它的原理是更好的:(1)认为,超载分辨率不考虑返回类型,或(2)尽量挑选我们知道将会工作在一个转换我们知道,不变会不会

这是一个主观判断。随着的的lambda 的,我们的的考虑这类转换的返回类型,在第7.4.3.3:


  

E为匿名函数,T1和T2
  是委托类型或前pression树
  类型具有相同的参数列表,
  推断返回类型为X的存在对于E
  在该参数列表的上下文中,
  与下列情况之一成立:


  
  

      
  • T1具有一个返回类型Y1和T2具有返回类型Y2和转换
      从X到Y1比好
      从X转换到Y2


  •   
  • T1具有返回类型Y,而T2是无效返回


  •   

不幸的是方法组转换和lambda转换在这方面是不一致的。不过,我可以住在一起。

总之,我们没有betterness的规则,以确定哪些转换比较好,X为D1或X D2。因此,我们给出Y(X)的分辨率多义性错误。

I have a scenario where I want to use method group syntax rather than anonymous methods (or lambda syntax) for calling a function.

The function has two overloads, one that takes an Action, the other takes a Func<string>.

I can happily call the two overloads using anonymous methods (or lambda syntax), but get a compiler error of Ambiguous invocation if I use method group syntax. I can workaround by explicit casting to Action or Func<string>, but don't think this should be necessary.

Can anyone explain why the explicit casts should be required.

Code sample below.

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() { }
}

解决方案

First off, let me just say that Jon's answer is correct. This is one of the hairiest parts of the spec, so good on Jon for diving into it head first.

Second, let me say that this line:

An implicit conversion exists from a method group to a compatible delegate type

(emphasis added) is deeply misleading and unfortunate. I'll have a talk with Mads about getting the word "compatible" removed here.

The reason this is misleading and unfortunate is because it looks like this is calling out to section 15.2, "Delegate compatibility". Section 15.2 described the compatibility relationship between methods and delegate types, but this is a question of convertibility of method groups and delegate types, which is different.

Now that we've got that out of the way, we can walk through section 6.6 of the spec and see what we get.

To do overload resolution we need to first determine which overloads are applicable candidates. A candidate is applicable if all the arguments are implicitly convertible to the formal parameter types. Consider this simplified version of your program:

class Program
{
    delegate void D1();
    delegate string D2();
    static string X() { return null; }
    static void Y(D1 d1) {}
    static void Y(D2 d2) {}
    static void Main()
    {
        Y(X);
    }
}

So let's go through it line by line.

An implicit conversion exists from a method group to a compatible delegate type.

I've already discussed how the word "compatible" is unfortunate here. Moving on. We are wondering when doing overload resolution on Y(X), does method group X convert to D1? Does it convert to D2?

Given a delegate type D and an expression E that is classified as a method group, an implicit conversion exists from E to D if E contains at least one method that is applicable [...] to an argument list constructed by use of the parameter types and modifiers of D, as described in the following.

So far so good. X might contain a method that is applicable with the argument lists of D1 or D2.

The compile-time application of a conversion from a method group E to a delegate type D is described in the following.

This line really doesn't say anything interesting.

Note that the existence of an implicit conversion from E to D does not guarantee that the compile-time application of the conversion will succeed without error.

This line is fascinating. It means that there are implicit conversions which exist, but which are subject to being turned into errors! This is a bizarre rule of C#. To digress a moment, here's an example:

void Q(Expression<Func<string>> f){}
string M(int x) { ... }
...
int y = 123;
Q(()=>M(y++));

An increment operation is illegal in an expression tree. However, the lambda is still convertible to the expression tree type, even though if the conversion is ever used, it is an error! The principle here is that we might want to change the rules of what can go in an expression tree later; changing those rules should not change the type system rules. We want to force you to make your programs unambiguous now, so that when we change the rules for expression trees in the future to make them better, we don't introduce breaking changes in overload resolution.

Anyway, this is another example of this sort of bizarre rule. A conversion can exist for the purposes of overload resolution, but be an error to actually use. Though in fact, that is not exactly the situation we are in here.

Moving on:

A single method M is selected corresponding to a method invocation of the form E(A) [...] The argument list A is a list of expressions, each classified as a variable [...] of the corresponding parameter in the formal-parameter-list of D.

OK. So we do overload resolution on X with respect to D1. The formal parameter list of D1 is empty, so we do overload resolution on X() and joy, we find a method "string X()" that works. Similarly, the formal parameter list of D2 is empty. Again, we find that "string X()" is a method that works here too.

The principle here is that determining method group convertibility requires selecting a method from a method group using overload resolution, and overload resolution does not consider return types.

If the algorithm [...] produces an error, then a compile-time error occurs. Otherwise the algorithm produces a single best method M having the same number of parameters as D and the conversion is considered to exist.

There is only one method in the method group X, so it must be the best. We've successfully proven that a conversion exists from X to D1 and from X to D2.

Now, is this line relevant?

The selected method M must be compatible with the delegate type D, or otherwise, a compile-time error occurs.

Actually, no, not in this program. We never get as far as activating this line. Because, remember, what we're doing here is trying to do overload resolution on Y(X). We have two candidates Y(D1) and Y(D2). Both are applicable. Which is better? Nowhere in the specification do we describe betterness between these two possible conversions.

Now, one could certainly argue that a valid conversion is better than one that produces an error. That would then effectively be saying, in this case, that overload resolution DOES consider return types, which is something we want to avoid. The question then is which principle is better: (1) maintain the invariant that overload resolution does not consider return types, or (2) try to pick a conversion we know will work over one we know will not?

This is a judgment call. With lambdas, we do consider the return type in these sorts of conversions, in section 7.4.3.3:

E is an anonymous function, T1 and T2 are delegate types or expression tree types with identical parameter lists, an inferred return type X exists for E in the context of that parameter list, and one of the following holds:

  • T1 has a return type Y1, and T2 has a return type Y2, and the conversion from X to Y1 is better than the conversion from X to Y2

  • T1 has a return type Y, and T2 is void returning

It is unfortunate that method group conversions and lambda conversions are inconsistent in this respect. However, I can live with it.

Anyway, we have no "betterness" rule to determine which conversion is better, X to D1 or X to D2. Therefore we give an ambiguity error on the resolution of Y(X).

这篇关于编译器调用不明确的错误 - 匿名方法和方法组Func键&LT;&GT;或行动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆