I don't understand why the compiler can't resolve the correct overload to use here. (code below) There is only one version of Add() that is appropriate- BigFoo is an IFoo, and does not implement IEnumerable where T is an IFoo. But it insists on reporting an ambiguity. Any ideas? I tried adding a second generic type parameter- Add where T : IFoo where U : IEnumerable. But then the overload is completely ignored even for legitimate use.
I know I can work around this with casting and specifying generic type parameters but at that point I've defeated the purpose of having an overload. You could question the overload, but the semantics feel correct to me- the behavior I'm implementing in my class is for both Add() to add the object wholesale as an individual entry in the collection. (the second Add() is not supposed to be an AddRange().)
namespace NS
{
interface IFoo { }
class BigFoo : IFoo, IEnumerable<int>
{
public IEnumerator<int> GetEnumerator()
{
throw new NotImplementedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
class FooContainer
{
public void Add(IFoo item) { }
public void Add<T>(IEnumerable<T> group) where T : IFoo { }
}
class DemoClass
{
void DemoMethod()
{
BigFoo bigFoo = new BigFoo();
FooContainer fooContainer = new FooContainer();
// error CS0121: The call is ambiguous between the following methods or properties:
// 'NS.FooContainer.Add(NS.IFoo)' and
// 'NS.FooContainer.Add<int>(System.Collections.Generic.IEnumerable<int>)'
fooContainer.Add(bigFoo);
}
}
}
Generic overload resolution doesn't take constraints into account, so it deems the
Add<T>
version to be applicable, inferringT=int
.Both methods are applicable, and neither is definitely better than the other, as there is no conversion between
IEnumerable<int>
andIFoo
. While generic methods are deemed "less specific" than non-generic methods, this only becomes relevant when the parameter types are identical after type argument replacement, which they're not in this case.The problem here is that generic type constraints are completely ignored by the compiler (it only looks at parameter types). As far as the compiler is concerned, the
IEnumerable<T>
argument being passed could just as well be aIEnumerable<IFoo>
.For complete information on this subject, refer to section 25.6.4 Inference of type arguments of the C# Language Specification. Note that there is no mention of the utilisation of type constraints.
Interesting.... Just tried your sample out. Generics continues to keep me on my toes.
The compiler should be smart enough to recognize that
BigFoo
can't be cast toIEnumerable<IFoo>
, but it isn't. It simply sees that it's anIEnumerable<T>
, and feels that it's a potential overload candidate (even though the contstraint you defined enforces thatT
must beIFoo
andint
can't be cast toIFoo
). While it's inconvenient, it's not that big of a deal. Just cast bigFoo toIFoo
and the compiler will be happy:Alternately, you can make your generic overload of Add uglier:
Either way, you have more typing, the second solution eliminates the need to cast calls to
Add
, but you will have to explicitly declare type on calls to the generic add (which ends up being more code:In FooContainer, on the second "Add" you are constraining T to be of type IFoo. BigFoo implements the IFoo interface, therefore it kinda matches that Add definition (even though it doesn't really, because it doesn't implement IEnumable<IFoo>).
I'm not sure I understand completely what you want, but I suspect it is this:
which would allow you to add any object T where T is an enumerable set of IFoo objects.
Is that what you wanted?
Regards, Richard