使用反射用基类数组填充派生类数组

Posted

技术标签:

【中文标题】使用反射用基类数组填充派生类数组【英文标题】:Use reflection to fill array of derived class with array of base class 【发布时间】:2017-11-07 01:48:24 【问题描述】:

考虑以下问题: 我想在运行时用值填充 T 类型的数组。我能够获得所有持有这些实例之一的游戏对象(这里谈论 Unity3D)。这些游戏对象位于 results 数组中。 GetComponent 方法允许从游戏对象中提取 T 的实例,但会自动将其转换为 Ts 基类Componentblueprint 包含有关绑定目标的信息,例如数组的 FieldInfo。

当前代码如下所示:

var arrayOfBaseClass = 
    (
        from instance 
        in results 
        select instance.GetComponent(blueprint.Field.FieldType.GetElementType()))
    .ToArray();
// Missing Step here
blueprint.Field.SetValue(o, arrayOfBaseClass);

SetValue 现在抛出 ArgumentException:对象类型 UnityEngine.Component[] 无法转换为目标类型:VEL.Input.ActionSignalReceiver[]

附加信息 arrayOfBaseClass 实际上是 T 的实例数组,只是通过 GetComponent 转换为基类型。如果我事先知道 T 手动转换为 T 将起作用并解决此问题。遗憾的是 T 事先不知道,并且可能在不同类型之间有所不同(但都派生自 Component)

现在的问题是,是否有办法使用 FieldInfo 的信息将数组转换回其最派生的类型。

【问题讨论】:

对阵列中的每个实例执行副本中提到的操作,然后就完成了。 我不认为这是链接问题的重复。首先,这不是将任意类转换为基类,其次,这个问题专门询问反射“SetValue”。我使用的类是派生类的实例,我只需要通过数组通过 SetValue 绑定它们。 从您的代码中很难看出resultsarrayOfDerivedClass 以及arrayOfBaseClass 实际上是什么。数组没有Field-property。 我重写并试图澄清要点。感谢您的意见! 【参考方案1】:

通过一些试验和错误想通了,将在解释下方发布相关代码。 当我们在反射中工作时,我们需要一种方法来传递我们想要使用的目标类型。在这种情况下,我们可以通过在运行时构造一个泛型方法来做到这一点。

所以首先我们声明一个泛型函数,它接受一个目标对象、一个 fieldInfo(事先检查这是一个数组字段)和一个对象数组。这些对象被强制转换为目标类型 T,然后分配给字段:

public void BindToTypedArray<T> (object bindingTarget, FieldInfo field, object[] values)
    
        var typedArray = from v in values select (T)v;
        field.SetValue(bindingTarget, typedArray.ToArray());
    

要调用这个函数,我们需要知道类型 T,但是反射提供了一种在运行时通过 MakeGenericMethod 创建类型化泛型方法的方法。所以我们在上面的例子中所做的是,我们为上面的函数创建一个自定义类型的实例,并输入我们生成的对象数组:

    var arrayOfBaseClass = 
        (
            from instance 
            in results 
            select instance.GetComponent(blueprint.Field.FieldType.GetElementType()))
        .ToArray();

    // NEWCODE --------------------------------------------------------
    MethodInfo bindArray= typeof(DataGlueController).GetMethod("BindToTypedArray");
    MethodInfo bindArrayTyped=bindArray.MakeGenericMethod(blueprint.Field.FieldType.GetElementType());
    bindArrayTyped.Invoke(this, new object[]  o, blueprint.Field, arrayOfBaseClass );
    // END NEWCODE ----------------------------------------------------

    // blueprint.Field.SetValue(o, arrayOfBaseClass); <- this is done in the BindToTypedArray function

【讨论】:

以上是关于使用反射用基类数组填充派生类数组的主要内容,如果未能解决你的问题,请参考以下文章

使用具有指向派生类对象的指针的基类指针数组调用派生类方法

派生类和基类的转换

在两者中使用基类和派生类作为数组时的循环

关于C++基类、派生类的引用和指针

BOOST_CLASS_EXPORT

C#通过反射将派生类转换为基类异常