Ошибка компиляции при добавлении универсального объекта в список

Почему list.Add(new B()) компилировать и list.Add(new Wrapper<B>()) не компилировать? Я думал, что или оба, или ни один не скомпилируют, потому что я думал, что компилятор сможет выяснить, что неявное приведение B возвращает Wrapper<B> который того же типа производится из new Wrapper<B>(), Я использую C# 4 в VS 2012.

class Wrapper<T> where T : new()
{
    public static implicit operator Wrapper<T>(T obj)
    {
        return new Wrapper<T>();
    }
    public static implicit operator T(Wrapper<T> obj)
    {
        return new T();
    }
}
class A { }
class B : A { }
class MyClass
{
    public static void Main(string[] args)
    {
        List<Wrapper<A>> list = new List<Wrapper<A>>();
        //This line compiles and runs successfully
        list.Add(new B());
        //This line doesn't compile
        list.Add(new Wrapper<B>());
    }
}

2 ответа

Решение

Из вашего вопроса кажется, что вы думаете, что добавление экземпляра B к списку Wrapper<A> работает потому что B неявно приведен к Wrapper<B>, который как-то добавляется в список. Тем не менее, это не то, что происходит. На самом деле, компилятор не может приводить из Wrapper<B> в Wrapper<A> в один шаг.

Причина добавления экземпляра B к списку Wrapper<A> работает потому, что компилятор видит, что B продолжается A и что существует пользовательское неявное приведение от A в Wrapper<A>,

Вы можете подумать, что вы также можете добавить Wrapper<B> к списку Wrapper<A> потому что есть пользовательское неявное приведение из Wrapper<B> в B а также B продолжается A и есть пользовательское неявное приведение из A в Wrapper<A>, Тем не менее, вы не можете связать воедино определенные пользователем неявные приведения таким образом, в соответствии со спецификациями (подробности см. В разделе 6.4.4). На самом деле, минимальный пример не должен иметь дело даже с дженериками. Рассмотрим этот простой пример проблемы:

class A 
{
    public static implicit operator B(A a) { return default(B); }
}
class B
{
    public static implicit operator C(B a) { return default(C); }
}
class C
{
    public static void Method(C c) { }
}
public static void Main()
{
    C.Method(new A());
}

Конечно, если вы явно выполняете приведение, это работает:

public static void Main()
{
    C.Method((B)new A());
}

list.Add(new B()); не добавляет новый Wrapper<B> к списку. Это добавление нового Wrapper<A> к списку.

Компилятор может определить, что ожидаемый тип Wrapper<A> и что есть неявное преобразование из того, что там new B() к Wrapper<A> потому что операнд для Wrapper<A> должен принять A объект и new B() это тип A,

list.Add(new Wrapper<B>()); не работает, потому что вы не можете добавить Wrapper<B> к List<Wrapper<A>> так как List не является ковариантным.

Другие вопросы по тегам