21

I've come across a strange situation which is non-ambiguous, yet the overload resolver doesn't think so. Consider:

public static class Program
{
    delegate int IntDel();
    delegate string StringDel();

    delegate void ParamIntDel(int x);
    delegate void ParamStringDel(string x);

    static void Test(IntDel fun) { }
    static void Test(StringDel fun) { }
    static void ParamTest(ParamIntDel fun) { }
    static void ParamTest(ParamStringDel fun) { }

    static int X() { return 42; }
    static void PX(int x) { }

    public static void Main(string[] args)
    {
        ParamTest(PX); // OK
        Test(X); // Ambiguos call!
    }
}

How come the call to ParamTest overloads is resolved correctly, but Test overload is ambiguous?

Vilx-
  • 104,512
  • 87
  • 279
  • 422
  • 7
    The return type of a method is not considered when the compiler selects an overload. An automatic consequence of not actually having to use the return value when you call a method. Trivially solved here, just use `Test(new IntDel(X));` – Hans Passant Feb 24 '15 at 13:34
  • I believe this is true for Java and C++ too. However is not a fundamental limitation of parametric polymorphism, for example in Haskell you can overload based on the return type. The problem is that allowing overloading on the return type may cause some problems with type inference. – Bakuriu Feb 24 '15 at 13:56
  • 2
    Offtopic: I love how almost any question will get at least 1 close vote these days. XD – Vilx- Feb 24 '15 at 20:04
  • possible duplicate of [Compiler Ambiguous invocation error - anonymous method and method group with Func<> or Action](http://stackoverflow.com/questions/2057146/compiler-ambiguous-invocation-error-anonymous-method-and-method-group-with-fun) – user4003407 Feb 25 '15 at 16:34

1 Answers1

37

Perhaps because https://msdn.microsoft.com/en-us/library/aa691131%28v=vs.71%29.aspx

The signature of a method specifically does not include the return type, nor does it include the params modifier that may be specified for the right-most parameter.

And the only difference between IntDel and StringDel is in the return value.

More specifically: https://msdn.microsoft.com/en-us/library/ms173171.aspx

In the context of method overloading, the signature of a method does not include the return value. But in the context of delegates, the signature does include the return value. In other words, a method must have the same return type as the delegate.

Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
xanatos
  • 109,618
  • 12
  • 197
  • 280
  • @Vilx- Added another reference that speaks explicitly of method overloading with delegates. – xanatos Feb 24 '15 at 13:36
  • How the `int X()` can be considered as both `IntDel` and `StringDel`? – Hamlet Hakobyan Feb 24 '15 at 13:44
  • 1
    @HamletHakobyan The right question is "how the (ignorereturnvalue) X() can be considered as both...". Clearly it can be of only one of the two delegate types, and if used with the other delegate type it would break, but the compiler at that time can't look at the return value type. – xanatos Feb 24 '15 at 13:45
  • 1
    I think the question needs more explanation to be considered fully answered. – Hamlet Hakobyan Feb 24 '15 at 13:48
  • Why then `()=>X()` does not cause ambiguity here? – user4003407 Feb 24 '15 at 13:50
  • @PetSerAl Because it falls on another part of the compiler probably. The lambda functions causes a [lambda type inference](http://blogs.msdn.com/b/jaredpar/archive/2007/12/14/c-lambda-type-inference.aspx), that probably selects the type of the first delegate if finds and causes the type of () =>X() to become of type `IntDel`. – xanatos Feb 24 '15 at 13:54