Ответ 1
Хорошо, пропустите это внимательно. Мы имеем
Update(new List<T>());
И три кандидата - обратите внимание, что мы заботимся только о подписях этих кандидатов, поэтому мы отменим типы и ограничения возврата, которые не являются частью подписи:
Update(IEnumerable<T> entities)
Update<U>(U entity)
Update<V>(IEnumerable<V> entities)
Наша первая задача - сделать вывод типа для этих двух последних кандидатов. Если вывод не выполняется, они не являются применимыми кандидатами.
Рассмотрим второй метод
Update<U>(U entity)
У нас есть аргумент типа List<T>
и формальный параметр U
. Поэтому мы заключаем, что U
есть List<T>
.
Рассмотрим третий метод:
Update<V>(IEnumerable<V> entities)
У нас есть аргумент типа List<T>
и формальный параметр типа IEnumerable<V>
. List<T>
реализует IEnumerable<T>
, поэтому мы выводим, что V есть T.
ОК, поэтому наш список кандидатов теперь состоит из:
Update(IEnumerable<T> entities)
Update<List<T>>(List<T> entity)
Update<T>(IEnumerable<T> entities)
Применяются ли все эти кандидаты? Да. В каждом случае List<T>
преобразуется в формальный тип параметра. Мы пока не можем устранить их.
Теперь, когда у нас есть только применимые кандидаты, мы должны определить, какой из них является лучшим.
Мы можем немедленно устранить третий. Третий и первый идентичны в своих формальных списках параметров. Правило С# заключается в том, что, когда у вас есть два метода, которые идентичны в их формальных списках параметров, и один из них попал туда "естественно", и один из них попал туда через подстановку типов, замещаемый теряет.
Мы также можем устранить первый. Ясно, что точное совпадение во втором случае лучше, чем неточное совпадение в первом.
Это оставляет второй, как последний человек, стоящий. Он выигрывает борьбу с перегрузкой. Затем во время окончательной проверки мы обнаруживаем, что ограничение нарушено: List<T>
не гарантируется быть производным классом T
.
Поэтому разрешение перегрузки выходит из строя. Ваши аргументы привели к тому, что лучший метод был выбран как недопустимый.
Если я назову
Update((new List<T>() { entity }).AsEnumerable())
, это будет нормально.
Правильно. Пройдите через это снова. Три кандидата:
Update(IEnumerable<T> entities)
Update<U>(U entity)
Update<V>(IEnumerable<V> entities)
У нас есть аргумент типа IEnumerable<T>
, поэтому мы выводим второй и третий:
Update(IEnumerable<T> entities)
Update<IEnumerable<T>>(IEnumerable<T> entity)
Update<T>(IEnumerable<T> entities)
Теперь у нас есть три применимых кандидата с идентичными списками параметров. Те, что строились там, автоматически хуже, чем естественные, поэтому мы устраняем второй и третий, оставляя только первые. Он выигрывает, и он не имеет ограничений, которые должны быть нарушены.
Это будет нормально, если вы удалите третий метод
Ваше утверждение ложно; это приведет к той же ошибке, что и первый сценарий. Удержание третьего кандидата не приводит к тому, что первый кандидат внезапно начинает избивать второго кандидата.