如何在基类列表中使用派生对象进行泛型扩展
How to use derived object in list of baseclass for generic extension
天哪,我在这个问题上花了很多时间...
我想做的是克隆一个 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
类型58=]。您可能认为我解释的不正确,但我认为这和您一样奇怪。我唯一能想到的是它是一个错误,我认为这不太可能。
最终编辑:
非常感谢@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;
}
source.GetType()
而不是 typeof(TComponent)
应该 return 实际(继承的)类型,您可以使用 Activator.CreateInstance()
创建一个实例。此外,您始终可以对整个树进行二进制序列化并再次反序列化,从而为您提供对象图的完整深度克隆。
天哪,我在这个问题上花了很多时间...
我想做的是克隆一个 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
类型58=]。您可能认为我解释的不正确,但我认为这和您一样奇怪。我唯一能想到的是它是一个错误,我认为这不太可能。
最终编辑:
非常感谢@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;
}
source.GetType()
而不是 typeof(TComponent)
应该 return 实际(继承的)类型,您可以使用 Activator.CreateInstance()
创建一个实例。此外,您始终可以对整个树进行二进制序列化并再次反序列化,从而为您提供对象图的完整深度克隆。