Подтвердить что ты не робот

Разрешение перегрузки метода в отношении дженериков и 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.

4b9b3361

Ответ 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>).