Можете ли вы удовлетворить общие ограничения с неявным преобразованием? - PullRequest
6 голосов
/ 17 ноября 2011

С учетом этих типов:

class A { }
class B
{
    public static implicit operator A(B me)
    {
        return new A();
    }
}

class Test<T> where T : A { }

Я пытался

var b = new Test<B>();

И ожидал, что это не получится, что он и сделал.Но сообщение об ошибке:

Тип «B» нельзя использовать в качестве параметра типа «T» в универсальном типе или методе «Test».Не существует неявного преобразования ссылок из 'B' в 'A'.

Но там есть неявное преобразование ссылок из B в A. Является ли это просто странным?message? Как показывает ответ Адама Робинсона, не неявное преобразование ссылок.Сообщение правильное.

Обратите внимание, что MSDN говорит:

где T: (имя базового класса) - аргумент типа должен быть или получен из указанногобазовый класс.

Это объясняет, почему это не разрешено, поскольку B не является производным от A

Ответы [ 4 ]

10 голосов
/ 17 ноября 2011

Нет, то, что вы пытаетесь сделать, невозможно. Неявное преобразование reference - это не то же самое, что неявное преобразование type . Ваш код определяет неявное преобразование типов, в котором вы можете сделать следующее:

B foo = new B();
A bar = foo;

Обратите внимание, однако, что foo и bar теперь содержат разные ссылки. Неявное преобразование типов создает новый экземпляр A, который должен (по соглашению) логически эквивалентен foo. Но дело в том, что это другая ссылка.

Преобразование

A reference происходит тогда, когда сама ссылка не изменяется, что означает, что рассматриваемый тип должен либо наследовать (для классов), либо реализовывать (для интерфейсов) рассматриваемый тип. Если я сделаю это:

class A { }
class B : A { }

Тогда мой код выше будет теперь содержать ту же ссылку в foo и bar. Это то, что подразумевается под неявным ссылочным преобразованием. И наоборот, явное эталонное преобразование будет пониженным, например:

A foo = new B();
B bar = (B)foo;

Опять же, ссылки те же, но приведение было явным.

Итак, вкратце, документация MSDN более четкая, но менее точная.

7 голосов
/ 17 ноября 2011

Это невозможно.

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

Это имеет смысл - подумайте о том, что будет делать компилятор в следующем:

class A 
{
    public void Foo();
}
class B
{
    public static implicit operator A(B me)
    {
        return new A();
    }
}

Теперь, скажем, у вас есть:

public void Bar<T>(T obj) where T : A
{       
    obj.Foo();
    obj.Foo();
    obj.Foo();
}

Чтобы выполнить эту работу с преобразованиями (т. Е. Разрешить вызов Bar(new B())), вам нужно будет создать экземпляр NEW объекта внутри этого метода, так как Foo не определен в B.Это было бы очень неожиданно и могло привести к некоторым очень трудным для обнаружения ошибкам.Выше должна ли операция преобразования происходить при каждом вызове метода?Должно ли это произойти один раз, и компилятор сделает несколько хитростей, чтобы заставить его работать?Хотя можно представить способы справиться с этим, ни один способ не ясен ...

1 голос
/ 17 ноября 2011

Другие люди в основном освещали это, но я подумал, что я вставлю кое-какие спецификации

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

Ссылочные преобразования, неявные или явные, никогда не меняют ссылочная идентичность объекта, подлежащего преобразованию. Другими словами, в то время как преобразование ссылки может изменить тип ссылки, оно никогда не изменяет тип или значение объекта, на который ссылаются.

Полный список преобразований выглядит следующим образом:

Неявные ссылочные преобразования:

  • От любого ссылочного типа к объекту и динамическому.
  • От любого типа класса S к любому типу класса T, при условии, что S является производным от T.
  • От любого типа класса S к любому типу интерфейса T при условии, что S реализует T.
  • Из любого интерфейса типа S в любой интерфейс типа T, если S получен из T.
  • От типа массива S с типом элемента SE до типа массива T с типом элемента TE при условии, что выполняются все следующие условия:
    • S и T отличаются только типом элемента. Другими словами, S и T имеют одинаковое количество измерений.
    • SE и TE являются ссылочными типами.
    • Существует неявное преобразование ссылок из SE в TE.
  • От любого типа массива до System.Array и интерфейсов, которые он реализует.
  • От одномерного массива типа S [] до System.Collections.Generic.IList и его базовых интерфейсов, при условии что существует неявное преобразование идентичности или ссылки из S в Т.
  • От любого типа делегата до System.Delegate и интерфейсов, которые он реализует.
  • От нулевого литерала до любого ссылочного типа.
  • Из любого ссылочного типа в ссылочный тип T, если он имеет неявную идентичность или преобразование ссылки в ссылочный тип T0 и T0 имеет преобразование идентичности в T.
  • Из любого ссылочного типа в интерфейс или тип делегата T, если он имеет неявную идентификацию или преобразование ссылки в интерфейс или Тип делегата T0 и T0 является конвертируемым по дисперсии (§13.1.3.2) в T.
  • Неявные преобразования, включающие параметры типа, которые, как известно, являются ссылочными типами. См. §6.1.10 для более подробной информации о неявном преобразования с использованием параметров типа.
0 голосов
/ 17 ноября 2011

В вашем примере кода B не является производным от A.Попробуйте

class B : A
{ 
    public static implicit operator A(B me) 
    { 
        return new A(); 
    } 
} 

Нет, это не странное сообщение.«Неявное ссылочное преобразование» (раздел 6.1.6 Спецификации) - это не то же самое, что «пользовательское неявное преобразование» (раздел 6.1.10), которое у вас есть.

Преобразование ссылки означаетчто вы можете преобразовать ссылку на данный объект из одного типа в другой («преобразования ссылок ... никогда не меняйте ссылочную идентичность преобразуемого объекта»).

Определяемое пользователем неявное преобразование может (какваш) возвращает новый, другой объект.

...