Неявное приведение C # к интерфейсу не удалось - PullRequest
17 голосов
/ 25 ноября 2010

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

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

18 голосов
/ 25 ноября 2010

Определенные пользователем преобразования не допускаются на интерфейсах вообще.Это может быть неоднозначным, потому что тип, из которого вы пытаетесь преобразовать, может реализовать сам интерфейс - в какой момент, что будет означать приведение?Ссылочное преобразование, такое как обычное приведение, или вызов определяемого пользователем преобразования?

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

Для данного типа источника Sи целевой тип T, если S или T являются обнуляемыми типами, пусть 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 по-прежнему рассматриваются.

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...