Using .NET 4, I am confused by the inability of the compiler to resolve the first method call in the sample below.
using System;
namespace MethodResolutionTest
{
class Program
{
static void Main(string[] args)
{
NonGeneric foo = null;
// ambiguous
foo.Ext1(x => new NonGeneric());
// resolves to first Ext1
foo.Ext1(x => new NonGeneric(), 1);
// resolves to first Ext2
foo.Ext2(x => new NonGeneric());
// resolves to first Ext2
foo.Ext2(x => new NonGeneric(), 1);
// resolves to second Ext2
foo.Ext2(x => "foo");
// resolves to second Ext2
foo.Ext2(x => "foo", 1);
// resolves to first Ext3
foo.Ext3(x => new NonGeneric());
// resolves to first Ext3
foo.Ext3(x => new NonGeneric(), 1);
// resolves to second Ext3
foo.Ext3(x => "foo");
// resolves to second Ext3
foo.Ext3(x => "foo", 1);
}
}
public class NonGeneric
{
}
public class Generic<T> : NonGeneric
{
}
public static class Extensions1
{
public static NonGeneric Ext1(this NonGeneric first, Func<NonGeneric, NonGeneric> getNext, int i = 0)
{
return null;
}
public static Generic<TNext> Ext1<TNext>(this NonGeneric first, Func<NonGeneric, TNext> getNext, int i = 0, string s = null)
{
return null;
}
}
// only difference between Extensions2 and Extensions1 is that the second overload no longer has a default string parameter
public static class Extensions2
{
public static NonGeneric Ext2(this NonGeneric first, Func<NonGeneric, NonGeneric> getNext, int i = 0)
{
return null;
}
public static Generic<TNext> Ext2<TNext>(this NonGeneric first, Func<NonGeneric, TNext> getNext, int i = 0)
{
return null;
}
}
// Extensions3 explicitly defines an overload that does not default the int parameter
public static class Extensions3
{
public static NonGeneric Ext3(this NonGeneric first, Func<NonGeneric, NonGeneric> getNext)
{
return Ext3(first, getNext, default(int));
}
public static NonGeneric Ext3(this NonGeneric first, Func<NonGeneric, NonGeneric> getNext, int i = 0)
{
return null;
}
public static Generic<TNext> Ext3<TNext>(this NonGeneric first, Func<NonGeneric, TNext> getNext, int i = 0)
{
return null;
}
}
}
Can anyone shed some light on this? I suspect I don't really have a way forward here other than modifying my APIs to help the compiler (as per Extensions3
above), but if there is an easier/better way then I'd love to hear it.
It is ambiguous because you have two optional parameters in the second Ext1
extension method. Because both parameters are omitted in the first call, the compiler doesn't know which one you want to use.
From C# 4.0 Language Specification:
§7.5.3 Overload resolution:
Given the set of applicable candidate function members, the best function member in that set is located. If the set contains only one function member, then that function member is the best function member. Otherwise, the best function member is the one function member that is better than all other function members with respect to the given argument list, provided that each function member is compared to all other function members using the rules in §7.5.3.2. If there is not exactly one function member that is better than all other function members, then the function member invocation is ambiguous and a binding-time error occurs.
Further, under §7.5.3.2 Better function member:
Optional parameters with no corresponding arguments are removed from the parameter list
What this means is that when you omit the two last arguments in the method call and the NonGeneric
type is inferred (read about type inference under §7.5.2), both methods would look like this:
Ext1(this NonGeneric first, Func<NonGeneric, NonGeneric> getNext)
Thus, they would be ambiguous...
I would recommend reading §7.5.3.2 or even the whole §7.5.3 of the specification for further info.
The solution is to either change your method declarations or remove the first overload altogether and let the second do the work :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With