Я ломаю голову над дженериками, ковариацией и контравариантностью. Я решил свою проблему, но она кажется мне довольно громоздкой, поэтому я хотел бы услышать от кого-либо из вас, есть ли какой-нибудь более простой способ сделать это.
Давайте возьмем эту иерархическую модель:
class MB { /* ... */ }
class M0 : MB { List<M1> Children; int Prop0; /* ... */ }
class M1 : MB { List<M2> Children; Guid Prop1; /* ... */ }
и т.д.
Так что иерархия может быть смоделирована свободно. Подумайте о суффиксе числа как об уровне иерархии, если он упрощается.
M0 mroot = new M0();
mroot.Prop0 = 123;
M1 mchild = new M1();
mchild.Prop1 = Guid.NewGuid();
mroot.Children.Add(mchild);
Тогда у меня есть другая иерархическая (view-модель), которая должна «обернуть» вышеприведенную модель:
class VB { /* ... */ }
class V0 : VB { M0 Model; List<V1> Children; /* ... */ }
class V1 : VB { M1 Model; List<V2> Children; /* ... */ }
и т.д.
Теперь рассмотрим коллекцию, целью которой является хранение некоторых узлов модели. Кстати, я хотел бы добавить ссылки несколькими способами: добавив узлы на основе MB, а также узлы на основе VB.
Вкратце, вот как я это сделал:
interface IVM<out T> where T : MB { T Model }
class VB<T> : VB, IVM<T> where T : MB { T Model; /* ... */ }
class V0 : VB<M0> { List<V1> Children; /* ... */ }
class V1 : VB<M1> { List<V2> Children; /* ... */ }
и т.д.
class MyList : List<MB>
{
public Add(MB item) { /* ... */ }
public AddRange(IEnumerable<MB> list) { /* ... */ }
public Add(IVM<MB> item) { Add(item.Model); }
public AddRange(IEnumerable<IVM<MB>> list) { foreach (var x in list) Add(x); }
}
На этом этапе модель представления проста, но не подвержена ошибкам:
int x = vroot.Model.Prop0;
Guid y = vroot.Children[0].Model.Prop1;
Даже добавить элементы в коллекцию просто:
list.Add(mchild);
list.Add(vroot.Children);
Я мог бы использовать универсальный VB, такой как:
class VB { MB Model; }
Но я бы также лишился возможности избегать опасных / вводящих в заблуждение приведений:
int x = ((M0)vroot.Model).Prop0;
Guid y = ((M1)vroot.Children[0].Model).Prop1;
Есть какое-нибудь меньшее глупое решение?
Заранее большое спасибо.
РЕДАКТИРОВАТЬ: добавлен код для повышения ясности.