0

Consider following problem: I want to fill an array of type T with values at runtime. I am able to get all the GameObjects (talking about Unity3D here) which hold one of these instances. These GameObjects are in the results array. The GetComponent method allows to extract the instances of T from the gameObject, but autmatically casts it to Ts base class Component. blueprint contains information about the binding target, like the FieldInfo of the array.

The current code looks like this:

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

SetValue throws right now ArgumentException: Object type UnityEngine.Component[] cannot be converted to target type: VEL.Input.ActionSignalReceiver[]

Additional info arrayOfBaseClass is actually an array of instaces of T, just cast to the base type by GetComponent. If I knew T beforehand a manual cast to T would work and solve this problem. Sadly T is not know beforehand and may vary between different types (yet all derive from Component)

The question now is if there is a way to use the information of FieldInfo to cast the array back to its most derived type.

RB.
  • 36,301
  • 12
  • 91
  • 131
floAr
  • 799
  • 1
  • 6
  • 29
  • Do what is mentioned in the duplicate for every instance in your array and you´re done. – MakePeaceGreatAgain Jun 06 '17 at 07:01
  • I don't think this is a duplicate of linked question. First this is not about casting an arbitrary class to a base class, secondly this question is specificially asking about the reflection 'SetValue'. The classes I consume are instances of the derived class and I only need to bind them through the array via SetValue. – floAr Jun 06 '17 at 07:23
  • From your code it´s hard to see what `results`, `arrayOfDerivedClass` and also what `arrayOfBaseClass` actually are. An array doesn´t have a `Field`-property. – MakePeaceGreatAgain Jun 06 '17 at 07:33
  • I rewrote and tried to clarify the points. Thank you for your input! – floAr Jun 06 '17 at 07:44

1 Answers1

0

Figured it out with some trial and error, will post relevant code below explanation. As we are working within reflection we need a way to pass into the type of the target we want to use. In this case we can do it by constructing a generic method at runtime.

So first we declare a generic function, which takes a target object, a fieldInfo (beforehand it is checked that this is an array field) and an array of objects. These objects are cast to the target type T and then assigned to the field:

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

To call this function we need to know the type T, but reflection offers a method to created a typed generic method at runtime via MakeGenericMethod. So what we do in the example above is that we create a custom typed instance of the function above and feed in the object array we generated:

    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
floAr
  • 799
  • 1
  • 6
  • 29