2010-03-26 7 views
4

фона:

Давайте предположим, что у меня есть следующий класс:Почему это неявное преобразование типов в C# не удается?

class Wrapped<T> : IDisposable 
{ 
    public Wrapped(T obj) { /* ... */ } 

    public static implicit operator Wrapped<T>(T obj) 
    { 
     return new Wrapped<T>(obj); 
    } 

    public void Dispose() { /* ... */ } 
} 

Как вы можете видеть, это обеспечивает оператор неявного преобразования типа для TWrapped<T>. В конце концов, я хотел бы иметь возможность использовать этот класс следующим образом:

interface IX { /* ... */ } 

class X : IX { /* ... */ } 

... 

IX plainIX = new X(); 

using (Wrapped<IX> wrappedIX = plainIX) 
{ 
    /* ... */ 
} 

Проблема:

Однако преобразование типов в приведенном выше пункте using терпит неудачу. Хотя я могу назначить new X() непосредственно на wrappedIX, мне не разрешено присваивать ему что-либо типа IX. Компилятор будет жаловаться на следующую ошибку:

Compiler error CS0266: Cannot implicitly convert type 'IX' to 'Wrapped<IX>'. An explicit onversion exists (are you missing a cast?)

Я не понимаю этого. В чем проблема?

+0

может быть скомпилирован, если заменить «IX plainIX = новый X();» с "X plainIX = новый X();" – Nagg

+0

@ Nagg: Из-за того, что я пытаюсь достичь (а именно, для создания свободного интерфейса поверх существующей библиотеки COM *), абсолютно необходимо программировать против интерфейсов. Поэтому, к сожалению, ваше предложение не является жизнеспособным вариантом. – stakx

ответ

5

Я считаю, это потому, что IX - это интерфейс. Компилятор считает, что значение типа IX уже может быть получено из Wrapped<IX> (даже если Wrapped<T> запечатан), поэтому он не использует преобразование.

Детали довольно сложны в разделах 6.4.3 и 6.4.4 спецификации C# 3.0. В основном потому, что IX - это интерфейс, он не «охвачен» любыми типами, что означает, что более поздний шаг в 6.4.4 сработает.

Я предлагаю вам создать не-универсальный тип Wrapped с помощью этого метода:

public static Wrapped<T> Of<T>(T item) 
{ 
    return new Wrapped<T>(item); 
} 

Тогда вы можете просто написать:

using (Wrapped<IX> wrappedIX = Wrapped.Of(plainIX)) 

В принципе преобразования могут быть немного сложнее по разным причинам - просто методы, как правило, легче понять, ИМО.

+0

Это будет работать с C# 4, когда 'T' правильно украшен' out', или он не работает для неявного литья? – Dykam

+0

@ Dykam: Вы предполагаете, что 'T' * can * можно использовать ковариантно. Я также не уверен, что вы подразумеваете под «этим», но поскольку тип 'plainIX' - это' IX', а не 'X', я не верю, что исходный код будет работать из коробки. –

+0

Хм, действительно. Тогда я пропустил эту часть. В любом случае неявные преобразования становятся беспорядочными. – Dykam