Ambiguity of overloaded methods passed by delegates in an overloaded call

Suppose I had this in C #:

class OverloadTest { void Main() { CallWithDelegate(SomeOverloadedMethod); } delegate void SomeDelegateWithoutParameters(); delegate void SomeDelegateWithParameter(int n); void CallWithDelegate(SomeDelegateWithoutParameters del) { } void CallWithDelegate(SomeDelegateWithParameter del) { } void SomeOverloadedMethod() { } void SomeOverloadedMethod(int n) { } } 

Of course, this will not compile, because the string CallWithDelegate(SomeOverloadedMethod); ambiguous.

Now suppose that there is only one CallWithDelegate(SomeDelegateWithoutParameter del) function (without overloads). In this case, there would be no ambiguity, because from what seems to be happening, the compiler can look at the parameter type and drop SomeOverloadedMethod(int n) from the candidate list (since it can only accept SomeDelegateWithoutParameters ), and therefore it compiles.

I am not going to write such code; it's just out of curiosity, from the point of view of the compiler author. I could not find an answer about this, since it is rather confusing to speak in words.

I would like to know if there is any way in C # to eliminate this call in Main() in the above example so that it compiles. How can you specify it so that it is allowed to pass CallWithDelegate(SomeDelegateWithoutParameters del) SomeOverloadedMethod() or CallWithDelegate(SomeDelegateWithParameter del) SomeOverloadedMethod(int n) ?

+5
source share
2 answers

There are several ways to resolve errors when overloading method groups.

Method 1: enter a group of methods

 CallWithDelegate((SomeDelegateWithoutParameters)SomeOverloadedMethod); CallWithDelegate((SomeDelegateWithParameter)SomeOverloadedMethod); 

This eliminates overload. This is a rather unusual syntax in the wild, but it works (C # 5 spec ยง6.6 Converting method groups):

As with all other implicit and explicit conversions, the casting operator can be used to explicitly perform a transformation of a group of methods.

<sub> [...] sub>

Method groups can influence overload resolution and participate in type inference.

Method 2: explicitly specify the delegate

 CallWithDelegate(new SomeDelegateWithoutParameters(SomeOverloadedMethod)); CallWithDelegate(new SomeDelegateWithParameter(SomeOverloadedMethod)); 

This is the same as the previous method without syntactic sugar. See the specification in ยง7.6.10.5. Delegate expression formats for more details.

Processing time binding of an expression-create-expression of the form new D(E) , where D is the delegate type and E is the expression, consists of the following steps:

  • If E is a group of methods, the delegate creation expression is treated in the same way as the conversion of a group of methods (ยง6.6) from E to D

<sub> [...] sub>

Even an example close to your question:

As described above, when a delegate is created from a group of methods, the formal list of parameters and the type of delegate return determine which of the overloaded methods to choose. In the example

 delegate double DoubleFunc(double x); class A { DoubleFunc f = new DoubleFunc(Square); static float Square(float x) { return x * x; } static double Square(double x) { return x * x; } } 

the Af field is initialized by a delegate who refers to the second Square method, because this method exactly matches the formal parameter list and the DoubleFunc return DoubleFunc . If the second Square method were not present, a compile-time error occurred.

Method 3: use lambda

 CallWithDelegate(() => SomeOverloadedMethod()); CallWithDelegate(i => SomeOverloadedMethod(i)); CallWithDelegate((int i) => SomeOverloadedMethod(i)); // Explicit types, if needed 

This form is not ambiguous, but has indirectness (lambda is called, and then the target method is called). It can be optimized by JIT, though, and it most likely will not have a visible impact on performance in any way.

Method 4: use anonymous delegates

 CallWithDelegate(delegate() { SomeOverloadedMethod(); }); CallWithDelegate(delegate(int i) { SomeOverloadedMethod(i); }); 

This is equivalent to lambda calls, but it uses the more massive (and older) delegate syntax.


If you want to know the exact rules for resolving overloads, they are described in the specification in ยง7.5.3. Overload Resolution.

+7
source

While I'm not an expert on the compiler, I can tell you that since you set 2 overloaded methods that correspond to both methods, the compiler has no way to determine your actual intent. It cannot compile because, at compile time, there is currently no actual identifying information, as already mentioned by Lucas, which you can use to eliminate ambiguity. For the compiler to solve this problem, you need runtime information that you really want to use based on arguments that you can try and pass.

0
source

All Articles