Разрешение перегрузки метода в отношении дженериков и IEnumerable
Я заметил это на днях, скажем, у вас есть два перегруженных метода:
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
.
Ответы
Ответ 1
Первая часть вашего вопроса (без перегрузки по списку) проста. Давайте рассмотрим вызов Array, потому что он работает одинаково для обоих вызовов:
Во-первых, вывод типа создает две возможные общие реализации вызова: Print<Person[]>(Person[] items)
и Print<Person>(IEnumerable<Person> items)
.
Затем перегружает разрешение, и первый выигрывает, потому что второй требует неявного преобразования, где первый не имеет значения (см. п. 7.4.2.3 спецификации С#). Тот же механизм работает для варианта списка.
При добавленной перегрузке третья возможная перегрузка создается при вызове List: Print<Person>(List<Person> items)
. Аргумент тот же, что и с Print<List<Person>>(List<Person> items)
, но опять же, в разделе 7.4.3.2 приведено разрешение с языком
Рекурсивно, построенный тип более специфичен, чем другой построенный тип (с тем же числом аргументов типа), если хотя бы один аргумент типа более конкретный, и аргумент типа менее определен, чем соответствующий аргумент типа в другом.
Таким образом, перегрузка Print<Person>
более специфична, чем перегрузка Print<List<Person>>
, а версия списка выигрывает над IEnumerable, потому что она не требует неявного преобразования.
Ответ 2
Потому что методы, генерируемые генераторами 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>)
.