С# общий неявный листинг на интерфейсе не удалось

Почему ниже не компилируется? Что особенного в интерфейсе, который заставляет компилятор думать, что он не может отбрасывать от Container<T> до T, когда T является интерфейсом? Я не думаю, что это ковариантная проблема, поскольку я не удручаю, но, возможно, это так. Это похоже на Почему компилятор С# не вызывает неявный оператор-оператор?, но я не думаю, что это совершенно так.

Product pIn =null;
Product pOut;
Container<Product> pContainer;

List<Product> pListIn = null;
List<Product> pListOut;
Container<List<Product>> pListContainer;

IList<Product> pIListIn = null;
IList<Product> pIListOut;
Container<IList<Product>> pIListContainer;

pContainer = pIn;
pOut = pContainer; // all good

pListContainer = pListIn; 
pListOut = pListContainer; // all good too

pIListContainer = pIListIn; // fails , cant do implicit cast for some reason
pIListOut = pIListContainer; // and here too

class Container<T>
{
 private T value;

 private Container(T item) { value = item; }

 public static implicit operator Container<T>(T item)
 {
  return new Container<T>(item);
 }

 public static implicit operator T(Container<T> container)
 {
  return container.value;
 }
}

Cannot implicitly convert type 'Container<IList<Product>>' to 'IList<Product>'. An explicit conversion exists (are you missing a cast?)
Cannot implicitly convert type 'IList<Product>' to 'Container<IList<Product>>'. An explicit conversion exists (are you missing a cast?)

Ответы

Ответ 1

Преобразования, определенные пользователем, вообще не допускаются на интерфейсы. Это потенциально было бы неоднозначным, потому что тип, который вы пытаетесь преобразовать из, мог реализовать сам интерфейс - на каком этапе было бы означать акцент? Референсное преобразование, такое как нормальный листинг, или вызов пользовательского преобразования?

Из раздела 10.3.3 спецификации С# 4:

Для данного типа источника S и целевого типа T, если S или T являются типами NULL, пусть S0 и T0 относятся к их базовым типам, в противном случае S0 и T0 равны S и T соответственно. Класс или структура разрешено объявлять преобразование из исходного типа S в целевой тип T только в том случае, если все из следующих значений истинны:

  • S0 и T0 - разные типы.
  • Либо S0, либо T0 - тип класса или структуры, в котором имеет место объявление оператора.
  • Ни S0, ни T0 не являются интерфейсом.
  • Исключая пользовательские преобразования, преобразование не существует от S до T или от T до S.

а затем позже:

Тем не менее, можно объявлять операторы на общих типах, которые для конкретных аргументов типа определяют преобразования, которые уже существуют как предопределенные преобразования...
В случаях, когда предопределенное преобразование существует между двумя типами, любые пользовательские преобразования между этими типами игнорируются. В частности:

  • Если предопределенное неявное преобразование (§6.1) существует от типа S до типа T, все пользовательские преобразования (неявные или явные) от S до T игнорируются.
  • Если предопределенное явное преобразование (§6.2) существует от типа S к типу T, любые пользовательские явные преобразования из S в T игнорируются. Более того:
    • Если T - тип интерфейса, пользовательские неявные преобразования из S в T игнорируются.
    • В противном случае по-прежнему учитываются пользовательские неявные преобразования от S до T.

Обратите внимание на первую вложенную пулю здесь.

(Я могу полностью рекомендовать получить спецификацию кстати. Он доступен в Интернете различные версии и форматы, но печатная аннотированная версия также является золотым рудником маленьких самородков из команды и других. Я должен признаться в определенном предвзятости, поскольку я один из аннотаторов, но игнорируя мои вещи, все остальные аннотации заслуживают внимания!)