Кастинг дженериков и родовой тип - PullRequest
6 голосов
/ 20 апреля 2010

Учтите, у меня есть следующие 3 класса / интерфейса:

class MyClass<T> { }

interface IMyInterface { }

class Derived : IMyInterface { }

И я хочу иметь возможность разыграть MyClass<Derived> в MyClass<IMyInterface> или наоборот:

MyClass<Derived> a = new MyClass<Derived>();
MyClass<IMyInterface> b = (MyClass<IMyInterface>)a;

Но я получаю ошибки компилятора, если я пытаюсь:

Cannot convert type 'MyClass<Derived>' to 'MyClass<IMyInterface>'   

Я уверен, что есть очень веская причина, почему я не могу этого сделать, но я не могу думать об этом.

Что касается того, почему я хочу сделать это - сценарий, который я представляю, - это такой сценарий, в котором вы в идеале хотите работать с экземпляром MyClass<Derived>, чтобы избежать множества неприятных приведений, однако вам нужно передать свой экземпляр интерфейс, который принимает MyClass<IMyInterface>.

Так что мой вопрос двоякий:

  • Почему я не могу кастовать между этими двумя типами?
  • Есть ли способ сохранить изящество в работе с экземпляром MyClass<Derived>, но при этом можно преобразовать его в MyClass<IMyInterface>?

Ответы [ 2 ]

5 голосов
/ 20 апреля 2010

Это не работает, потому что C # поддерживает только ковариацию по параметрам типа интерфейсов и делегатов. Если ваш параметр типа существует только в выходных позициях (то есть вы только возвращаете его экземпляры из своего класса и не принимаете его в качестве аргумента), вы можете создать такой интерфейс:

interface IClass<out T> { }
class MyClass<T> : IClass<T> { }

Что позволит вам сделать это:

IClass<Derived> a = new MyClass<Derived>();
IClass<IMyInterface> b = a;

Честно говоря, это примерно так близко, как вы собираетесь получить, и это требует работы компилятора C # 4.

3 голосов
/ 20 апреля 2010

Причина, по которой вы не можете сделать это в общем, заключается в том, что большинство классов не являются простыми пустыми примерами. У них есть методы:

class MyClass<T> 
{
    static T _storage;

    public void DoSomethingWith(T obj)
    {
        _storage = obj;
    }
}

interface IMyInterface { }

class Derived : IMyInterface { }

MyClass<Derived> a = new MyClass<Derived>();

Теперь у a есть метод DoSomethingWith, который принимает Derived и сохраняет его в статической переменной типа Derived.

MyClass<IMyInterface> b = (MyClass<IMyInterface>)a;

Если бы это было разрешено, b теперь, по-видимому, имеет метод DoSomethingWith, который принимает что угодно , которое реализует IMyInterface, и затем внутренне пытается сохранить его в статической переменной типа Derived, потому что это все тот же объект, на который ссылается a.

Так что теперь у вас есть переменная типа Derived, хранящая ... кто знает, что.

...