I noticed this the other day, say you have two overloaded methods:
public void Print(IEnumerable items) {
Console.WriteLine(\"IEnumerab
The first part of your question (without the List-specific overload) is easy. Let's consider the Array call, because it works the same for both calls:
First, type inference produces two possible generic implementations of the call: Print<Person[]>(Person[] items)
and Print<Person>(IEnumerable<Person> items)
.
Then overload resolution kicks in and the first one wins, because the second requires an implicit conversion, where the first one does not (see §7.4.2.3 of the C# spec). The same mechanism works for the List variant.
With the added overload, a third possible overload is generated with the List call: Print<Person>(List<Person> items)
. The argument is the same as with the Print<List<Person>>(List<Person> items)
but again, section 7.4.3.2 provides the resolution with the language
Recursively, a constructed type is more specific than another constructed type (with the same number of type arguments) if at least one type argument is more specific and no type argument is less specific than the corresponding type argument in the other.
So the Print<Person>
overload is more specific than the Print<List<Person>>
overload and the List version wins over the IEnumerable because it requires no implicit conversion.
Because the methods generated from the generics Print(Person[] item)
and Print(List<Person> item)
are a better match than IEnumerable<T>
.
The compiler is generating those methods based on your type arguments, so the generic template Print<T>(T item)
will get compiled as Print(Person[] item)
and Print(List<Person> item)
(well, whatever type represents a List<Person>
at compilation). Because of that, the method call will be resolved by the compiler as the specific method that accepts the direct type, not the implementation of Print(IEnumerable<Peson>)
.