Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Method overload resolution with regards to generics and IEnumerable

I noticed this the other day, say you have two overloaded methods:

public void Print<T>(IEnumerable<T> items) {
    Console.WriteLine("IEnumerable T"); 
}
public void Print<T>(T item) {
    Console.WriteLine("Single T"); 
}

This code:

public void TestMethod() {  
    var persons = new[] { 
        new Person { Name = "Yan", Age = 28 },
        new Person { Name = "Yinan", Age = 28 } 
    };  
    Print(persons);
    Print(persons.ToList()); 
}

prints:

Single T
Single T

Why are Person[] and List<Person> better matched to T than they are to IEnumerable<T> in these cases?

Thanks,

UPDATE: Also, if you have another overload

public void Print<T>(List<T> items) {
    Console.WriteLine("List T");
}

Print(persons.ToList()); will actually print List T instead of Single T.

like image 385
theburningmonk Avatar asked Feb 05 '11 22:02

theburningmonk


1 Answers

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.

like image 157
TToni Avatar answered Nov 13 '22 04:11

TToni