Кастинг дженериков и родовой тип

Учтите, у меня есть следующие 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 ответа

Решение

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

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

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

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

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

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

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 хранение... кто что знает

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