Короче говоря, я хотел бы иметь возможность хранить генерики с использованием параметров другого типа в массиве, используя родительский тип для всех используемых типов. MSDN упомянул, что это невозможно, поскольку дженерики были инвариантными типами, но в комментарии говорилось, что это изменилось со времен 4.0.Массив из разных генераторов
Вот простой пример того, что я хотел бы сделать:
public class Animal
{
}
public class Dog : Animal
{
}
public class Cat : Animal
{
}
public class MyGeneric<T>
{ }
public class MyInheritedGeneric<T> : MyGeneric<T>
{ }
static void Main(string[] args)
{
MyGeneric<Animal>[] myGenericArray = new MyGeneric<Animal>[]
{
new MyGeneric<Dog>(),
new MyInheritedGeneric<Cat>()
};
}
Это возвращает аналогичные ошибки:
Cannot implicitly convert type
'InheritanceTest.Program.MyGeneric<InheritanceTest.Program.Dog>' to
'InheritanceTest.Program.MyGeneric<InheritanceTest.Program.Animal>'
Cannot implicitly convert type
'InheritanceTest.Program.MyInheritedGeneric<InheritanceTest.Program.Cat>'
to 'InheritanceTest.Program.MyGeneric<InheritanceTest.Program.Animal>'
Есть ли способ для хранения дженерики в массиве с использованием родительского класс типа, или это просто невозможно? Я действительно надеюсь, что это возможно, иначе это сделает мою программу кошмаром ...
EDIT: немного больше контекста!
Я делаю классы для создания врагов в игре. Я называю их «Шаблоны» (ничего общего с фактическими классами шаблонов, я вполне мог бы назвать их «Чертежи или фабрики»). Конструктор-враг использует шаблон, который он использует для определения своих собственных значений. Когда игра загружается, шаблоны используются для генерации всех врагов, используя их функцию Generate(), которая возвращает массив соответствующего типа, который они назначают для создания. Все объекты, которые должны быть созданы с помощью шаблона, должны иметь конструктор, принимающий шаблон в качестве единственного параметра.
public class Template<T>
{
protected static Random random = new Random();
protected int _amount;
public int Amount
{
get { return _amount; }
}
public virtual T CreateInstance()
{
return (T)Activator.CreateInstance(typeof(T), this);
}
public virtual T[] Generate()
{
T[] objects = new T[Amount];
for (int i = 0; i < Amount; ++i)
objects[i] = CreateInstance();
return objects;
}
}
Реферат файла BasicZombie.cs, который содержит фактический класс противника и шаблон.
class Tpl_BasicZombie : Tpl_Enemy<BasicZombie>
{
public Tpl_BasicZombie()
{
_hp = 4;
_speed = 3;
_amount = 10;
}
}
class BasicZombie : GroundEnemy
{
public BasicZombie(Tpl_BasicZombie template)
: base(template, TextureManager.Get("zombie_base"), 1, 8)
{ }
public void StuffHappens()
{ }
}
При загрузке игры, я хотел бы пройти через все шаблоны в массиве для загрузки врагов из них. Я знаю, что я мог бы сделать это вручную, но каждый раз, когда я создаю новый тип врага, мне нужно будет добавить его вручную в код (таким образом, возможно, забывая больше одного раза).
Мои два варианта: 1- Использовать общий, и вышеуказанная проблема возникает. 2- Используйте не общий и сохраните тип внутри, который будет привязывать функцию Generate() возвращаемого типа. Это означало бы, что функция generate выведет массив объектов, массив, который нужно будет преобразовать в подходящий тип каждый раз, когда шаблон генерирует массив врагов.
У меня есть место в голове, которое говорит мне, что есть элегантное решение для всего этого, и я надеюсь, что это правильно!
Вы уже читали [Ковариационный и контравариантный FAQ] (http://blogs.msdn.com/b/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx?Redirected=true)? – 48klocs
У меня нет. Я посмотрел, и это было очень полезно! Хотел бы я знать эти ключевые слова раньше. Я думаю, что вполне могу использовать интерфейс для ковариации. –