如何在基类列表中使用派生对象进行泛型扩展

时间:2017-08-06 19:42:31

标签: c# generics xna game-engine

哦,男孩,我花了很多时间来解决这个问题......

我尝试做的是克隆GameObject类型的对象,该对象还包含Component列表。克隆GameObject类型不是任何问题,但似乎这个列表是一个问题,因为它是克隆但不是它的内容;内容不会被复制,只是存储。

尽管我想以与游戏对象相同的方式克隆这些组件,但由于我的系统,这是不可能的。
组件附加到GameObject并基本上定义GameObject。例如,如果我想创建一种用于控制GameObject的方法,我会创建一个名为PlayerController的组件,它将从基类型Component派生,看起来像这样:

class Component
{
    //The gameobject this component is attached to
    public GameObject gameObject { get; set;}

    public virtual void Update()
    {
        //..
    }
}

class PlayerController : Component
{

    override Update()
    {
        Move();
        //..
    }

    public void Move()
    {
        //...
    }
}

我克隆GameObject的方式是这样的方法:

public GameObject Clone(Vector2 position, float scale)
{
    GameObject source = this;
    GameObject clone = new GameObject();
    //Get all properties of a GameObject
    var srcProperties = TypeDescriptor.GetProperties(typeof(GameObject)).Cast<PropertyDescriptor>();
    //Assign all properties from the source to the clone
    foreach (var srcProperty in srcProperties)
    {
        srcProperty.SetValue(clone, srcProperty.GetValue(source));
    }
    //Clone all components from source and add them to the clone
    if (source.components.Count > 0)
    {
        for (int i = source.components.Count - 1; i >= 0; i--)
        {
            var srcComp = source.components[i];
            var compClone = srcComp.CloneComponent();
            clone.components.Add(compClone);
        }
    }

    clone.position = position;
    clone.scale = scale;
    AllGameObjects.Add(clone);

    if (clone.components.Count > 0)
    {
        //Set the owner gameobjects and start the components
        for (int i = clone.components.Count - 1; i >= 0; i--)
        {
            var comp = clone.components[i];
            comp.gameObject = clone;
            comp.Start();
        }
    }

    return clone;
}

第17行CloneComponent()中的var compClone = srcComp.CloneComponent();方法是一种通用的扩展方法,如下所示:

public static TComponent CloneComponent<TComponent>(this TComponent source) where TComponent : Component, new()
{
    TComponent clone = new TComponent();

    var srcProperties = TypeDescriptor.GetProperties(typeof(TComponent)).Cast<PropertyDescriptor>();

    foreach (var srcProperty in srcProperties)
    {
        srcProperty.SetValue(clone, srcProperty.GetValue(source));
    }

    return clone;
}

这个方法工作正常,如果源不是从Component列表中获取的,因为似乎将它的类型转换为根类型,它只在用作一个论点。这显然会导致TComponent属于Component类型,而不是克隆源,它只是将其转换为根类型,并且只从中克隆属性。

我要求的是一种绕过它的方法,或者当它用作参数时它不能转换为根类型?

修改

参数source的类型将是正确的类型,但是当源参数作为参数从列表给出时,TComponent将始终为Component类型。你可能会认为我解释这个不正确,但我认为它和你一样奇怪。我唯一能想到的是它是一个错误,我认为不太可能。

最终修改
非常感谢@Philipp建议使用Activator.CreateInstance。这就是我改变CloneComponent()方法的方法:

public static Component CloneComponent(this Component source)
{
    var clone = Activator.CreateInstance(source.GetType());

    var srcProperties = TypeDescriptor.GetProperties(source.GetType()).Cast<PropertyDescriptor>();

    foreach (var srcProperty in srcProperties)
    {
        srcProperty.SetValue(clone, srcProperty.GetValue(source));
    }

    return (Component)clone;
}

1 个答案:

答案 0 :(得分:1)

source.GetType()而不是typeof(TComponent)应返回实际(继承)类型,您可以使用Activator.CreateInstance()创建实例。此外,您始终可以对整个树进行二进制序列化并再次反序列化,从而为对象图提供完整的深层克隆。