3

长话短说,我希望能够通过对所有使用的类型使用父类型,在数组中使用不同类型参数存储泛型。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>'

有没有办法使用类型的父类将泛型存储在数组中,或者这根本不可能?我真的希望这是可能的,否则它会让我的程序成为一场噩梦......

编辑:更多的上下文!

我正在制作课程以在游戏中生成敌人。我称它们为模板(与实际的模板类无关,我完全可以称它们为蓝图或工厂)。一个敌人的构造函数接受一个模板,它用它来确定自己的值。当游戏加载时,模板用于生成所有敌人,使用它们的 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 函数将输出一个对象数组,该数组需要在模板每次生成敌人数组时转换为合适的类型。

我的脑海中有一个空间告诉我有一个优雅的解决方案来解决这一切,我希望它是正确的!

4

3 回答 3

7

是的,C# 4 支持泛型变体- 但仅在接口和委托的声明中,因此在这种情况下您将无法做到这一点。当然,您可能会创建一个接口:

public interface IGeneric<out T>

然后在你的类中实现它,此时你可以创建一个IGeneric<Animal>.

如果您可以提供有关您想要实现的目标的更多详细信息,我们或许可以帮助您找到替代方法。

于 2012-06-22T17:55:50.610 回答
1

除了 Jon Skeet 的信息,您也许可以执行以下操作:

public MyGeneric<T2> ToOtherType<T2>()
{
    if (typeof(T2).IsAssignableFrom(typeof(T)))
    {
        // todo: get the object
        return null;
    }
    else
        throw new ArgumentException();
}

        new MyGeneric<Dog>().ToOtherType<Animal>(),
        new MyInheritedGeneric<Cat>().ToOtherType<Animal>()
于 2012-06-22T18:30:46.527 回答
1

如果一个数组要保存不止一种类型的项,则这些项必须存储在与数组本身分开的堆对象中(如果某些类型是结构,则它们必须被装箱或存储为从非泛型派生的泛型类型中的字段)。在大多数情况下,最简单的做法是为要存储在数组中的所有内容识别一个共同的祖先类型,并根据需要简单地对数组元素进行类型转换。然而,在少数情况下这是不可行的。例如,如果您的集合要保存类型未知但受多个接口约束的对象,则有必要将这些对象传递给其方法类型参数同样受约束的泛型例程,

如果您的集合中的对象将仅传递给少数例程,则可以使用通用方法添加项目构造委托以适当地调用所有必要的例程并将这些委托存储为集合的一部分。Lambda 表达式或匿名委托可能对此很方便。

例如,假设需要能够将存储在列表中的项目提供给Wibble<T>各种IWibbler对象的Wobble<T>方法和各种对象的方法IWobbler,其中T类型具有接口约束I1I2.

    接口 IWibbler { void Wibble<T>(T param, int param) 其中 T : I1,I2; }
    interface IWobbler { void Wobble<T>(T param, string param) where T: I1,I2; }

    私有结构 WibbleWobbleDelegateSet
    {
        公共动作<IWibbler, int> Wibble;
        公共动作<IWobbler,字符串> 摆动;
        静态 WibbleWobbleDelegateSet Create<T>(T param) where T: I1, I2
        {
            var ret = 新的 WibbleWobbleDelegateSet ();
            ret.Wibble = (IWibbler wibbler, int p2) => { wibbler.Wibble<T>(param, p2); };
            ret.Wobble = (IWobbler wobbler, string p2) => { wobbler.Wobble<T>(param, p2); };
            返回 ret;
        }
    }

WibbleWobbleDelegateSet.Create<T>(T param)使用适当约束的调用param将产生一个非泛型结构,其中包含可用于将创建结构时提供的参数传递给任何IWibbler.Wibble<T>()IWobbler.Wobble<T>()方法的委托。

这种方法只有在将要调用的例程列表已知时才能直接使用。如果需要能够使用受约束的泛型参数调用任意例程,则可以使用一些棘手的接口或使用反射来做到这一点,但这样的事情会变得更加复杂。

于 2012-06-22T19:58:17.447 回答