Ошибка компилятора C #? Почему это неявное пользовательское преобразование не компилируется?

Учитывая следующую структуру:

public struct Foo { public Foo(T obj) { } public static implicit operator Foo(T input) { return new Foo(input); } } 

Этот код компилирует:

 private Foo MakeFoo() { string c = "hello"; return c; // Success: string is ICloneable, ICloneable implicitly converted to Foo } 

Но этот код не компилируется – почему?

 private Foo MakeFoo() { ICloneable c = "hello"; return c; // Error: ICloneable can't be converted to Foo. WTH? } 

По-видимому, неявные пользовательские преобразования не работают, когда один из типов является интерфейсом. Из спецификаций C #:


6.4.1 Разрешенные пользовательские преобразования

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

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

В первом методе оба типа не являются типами интерфейсов, поэтому пользовательское неявное преобразование работает.

Спецификации не очень ясны, но мне кажется, что если один из типов связан с типом интерфейса, то компилятор даже не пытается найти какие-либо пользовательские неявные преобразования.

(Следуя за комментариями принятого ответа.)

Да, это очень, очень запутанная часть спецификации. Весь бит о «охватывающих типы» в частности глубоко испорчен. Я уже несколько лет пытаюсь найти время, чтобы полностью переписать весь этот раздел во что-то более последовательное, но он никогда не был достаточно высоким приоритетом.

По сути, у нас есть противоречие; мы говорим, что нет никаких пользовательских неявных преобразований, включающих интерфейсы, но ясно, что это не так в данном случае; существует определенное пользователем неявное преобразование из IC в Foo , продемонстрированное тем фактом, что строка переходит к Foo через это преобразование.

То, что мы действительно должны лучше подчеркивать, – это строка, которую вы цитировали:

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

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

 interface IBar {} interface IFoo : IBar {} class Foo : IFoo { public static explicit operator Foo(T input) { whatever } } class Blah : Foo {} ... IBar bar = new Blah(); Foo foo = (Foo)bar; 

Теперь, вызывает ли это пользовательское явное преобразование или нет? Объект действительно получен из Foo, поэтому вы надеетесь, что этого не произойдет; это должен быть простой тест типа и ссылочное задание, а не вызов вспомогательного метода. Приведение значения интерфейса всегда рассматривается как тест типа, поскольку почти всегда возможно, что объект действительно имеет этот тип и действительно реализует этот интерфейс. Мы не хотим отказывать вам в возможности сделать дешевое преобразование, сохраняющее представление.

  • Почему автобоксирование делает некоторые вызовы неоднозначными на Java?
  • Понимание оптимизации возвращаемого значения и возврата временных рядов - C ++
  • что такое «выравнивание стека»?
  • Целевой цикл C #import
  • Почему autoboxing не отменяет varargs при использовании перегрузки методов в Java 7?
  • Сведения о создании шаблона компиляторов GCC и MS
  • Лучший уровень предупреждения компилятора для компиляторов C / C ++?
  • Есть ли способ установить gcc в OSX без установки Xcode?
  • Утка, набирая в компиляторе C #
  • Какие функции C99 доступны в компиляторе MS Visual Studio?
  • Делегирование изменений поведения кеширования в Roslyn
  • Interesting Posts

    Вращение 3D-вектора?

    Обновление Windows 8.1 с 32-разрядной версией до версии Windows 64 с 64-разрядной версией

    Google Maps: как получить страну, штат / провинцию / регион, город, учитывая значение lat / long?

    Nokogiri «Не удалось создать родное расширение gem», когда я запускаю установку пакета

    Как настроить cookie HttpOnly в tomcat / java webapps?

    Когда я делаю перетаскиваемый клон и бросаю его в droppable, я не могу его перетащить снова

    Как я могу получить ресурс «Папка» из моего файла jar?

    Дизайн программного обеспечения и архитектура программного обеспечения

    Функция сна в C ++

    Невозможно просмотреть общие ресурсы на компьютере под управлением Windows 7 x64

    Как я могу восстановить свой ноутбук из неудавшегося обновления BIOS?

    Как сделать круглые углы как внутри коробки, так и ее границы?

    «Введите« против »push ebp; mov ebp, esp; sub esp, imm “и” leave “vs” mov esp, ebp; pop ebp “

    Набирает% ^ в cmd.exe пасхальное яйцо Windows?

    Каков наилучший способ загрузить JSONObject из текстового файла json?

    Давайте будем гением компьютера.