C# generic overload - Compiler can't determine correct call

▼魔方 西西 提交于 2019-12-05 10:29:14

Generic overload resolution doesn't take constraints into account, so it deems the Add<T> version to be applicable, inferring T=int.

Both methods are applicable, and neither is definitely better than the other, as there is no conversion between IEnumerable<int> and IFoo. 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.

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:

public void Add<T>(T group) where T : IEnumerable<IFoo> { }

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

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 a IEnumerable<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.

The compiler should be smart enough to recognize that BigFoo can't be cast to IEnumerable<IFoo>, but it isn't. It simply sees that it's an IEnumerable<T>, and feels that it's a potential overload candidate (even though the contstraint you defined enforces that T must be IFoo and int can't be cast to IFoo). While it's inconvenient, it's not that big of a deal. Just cast bigFoo to IFoo and the compiler will be happy:

fooContainer.Add((IFoo)bigFoo);

Alternately, you can make your generic overload of Add uglier:

public void Add<T, U>(U group)
    where T : IFoo
    where U : IEnumerable<T>
{
}

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:

fooContainer.Add<IFoo, IEnumerable<IFoo>>(enumerableFoo);

Interesting.... Just tried your sample out. Generics continues to keep me on my toes.

//1 - First preference
public void Add(BigFoo item) { Console.WriteLine("static BigFoo type Add"); }
//2 - Second Preference
public void Add<T>(T item) { Console.WriteLine("Generic Add");  }
//3 - Third preferences
public void Add(IFoo item) { Console.WriteLine("static IFoo interface Add"); }
//4 - Compiles if 1-4 exist. Compile error (ambiguity) if only 3-4 exist. Compile error (cannot convert int to IFoo) if only 4 exists
public void Add<T>(IEnumerable<T> group) where T : IFoo { }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!