Кастинг дженериков и родовой тип
Учтите, у меня есть следующие 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
хранение... кто что знает