Разрешение перегрузки метода в отношении обобщений и IEnumerable - PullRequest
19 голосов
/ 06 февраля 2011

Я заметил это на днях, скажем, у вас есть два перегруженных метода:

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

Этот код:

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

печать:

Single T
Single T

Почему Person[] и List<Person> лучше соответствуют T, чем IEnumerable<T> в этих случаях?

Спасибо

UPDATE: Также, если у вас есть другая перегрузка

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

Print(persons.ToList()); на самом деле напечатает List T вместо Single T.

Ответы [ 2 ]

18 голосов
/ 06 февраля 2011

Первая часть вашего вопроса (без перегрузки по списку) проста.Давайте рассмотрим вызов Array, поскольку он работает одинаково для обоих вызовов:

Во-первых, вывод типа создает две возможные универсальные реализации вызова: Print<Person[]>(Person[] items) и Print<Person>(IEnumerable<Person> items).

Затем перегрузкаРазрешение вступает в силу, и первое выигрывает, потому что второе требует неявного преобразования, в отличие от первого (см. §7.4.2.3 спецификации C #).Тот же механизм работает для варианта List.

При добавленной перегрузке с помощью вызова List генерируется третья возможная перегрузка: Print<Person>(List<Person> items).Аргумент такой же, как и для Print<List<Person>>(List<Person> items), но опять же, в разделе 7.4.3.2 предоставляется разрешение на языке

Рекурсивно, составной тип более специфичен, чем другой составной тип (с тем же номеромаргументов типа), если хотя бы один аргумент типа является более конкретным и ни один аргумент типа не является менее конкретным, чем соответствующий аргумент типа в другом.

Таким образом, перегрузка Print<Person> является более специфичной, чем Print<List<Person>> перегрузка и версия List побеждает IEnumerable, потому что он не требует неявного преобразования.

3 голосов
/ 06 февраля 2011

Поскольку методы, сгенерированные из шаблонов Print(Person[] item) и Print(List<Person> item), лучше соответствуют, чем IEnumerable<T>.

Компилятор генерирует эти методы на основе аргументов вашего типа, поэтому универсальный шаблон Print<T>(T item) будет скомпилирован как Print(Person[] item) и Print(List<Person> item) (хорошо, любой тип представляет List<Person> при компиляции). По этой причине вызов метода будет разрешен компилятором как определенный метод, который принимает прямой тип, а не реализацию Print(IEnumerable<Peson>).

...