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

Нет никакого неявного преобразования ссылок из 'System.Collections.Generic.List <T>' в 'T'

class Class1<T>
{
    public virtual void Update(T entity)
    {
        Update(new List<T>() { entity }); //It failed
    }

    public virtual void Update(IEnumerable<T> entities)
    {
    }

    public virtual void Update<TSub>(TSub entity) where TSub : T
    {
    }

    public virtual void Update<TSub>(IEnumerable<TSub> entities) where TSub : T
    {
    }
}

У меня есть фрагмент кода. Но он всегда терпел неудачу.

Если я заменил Update(new List<T>() { entity }) на Update((new List<T>() { entity }).AsEnumerable()), это будет нормально.

Это будет нормально, если вы удалите третий метод Update<TSub>(TSub entity) where TSub : T.

Может ли кто-нибудь сказать мне, почему?

4b9b3361

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

Теперь у нас есть три применимых кандидата с идентичными списками параметров. Те, что строились там, автоматически хуже, чем естественные, поэтому мы устраняем второй и третий, оставляя только первые. Он выигрывает, и он не имеет ограничений, которые должны быть нарушены.

Это будет нормально, если вы удалите третий метод

Ваше утверждение ложно; это приведет к той же ошибке, что и первый сценарий. Удержание третьего кандидата не приводит к тому, что первый кандидат внезапно начинает избивать второго кандидата.

Ответ 2

Ограничения не являются частью подписи, у Эрика Липперта есть отличная статья об этой теме.

Ответ 3

Вы, по сути, спрашиваете, почему компилятор не создает неявный листинг от List<T> до IEnumerable<T>. Причина в том, что команда С# сделала преднамеренное дизайнерское решение, что случаи потенциальной двусмысленности должны быть решены программистом, а не компилятором. (Обратите внимание, что команда VB.NET приняла другое решение, чтобы всегда пытаться что-то разумное, что согласуется с предполагаемым намерением программиста.)

Преимущества в таком случае, что сюрприз сведен к минимуму - ничто не может случиться под обложками; недостатком является случайная потребность в более подробном коде.