vendredi 20 mars 2020

How to use Reflection to set Value Types in a call chain correctly

So I'm writing a parser that uses an xml document to create a group of objects and fill in their properties. I want a user to be able to with attributes put a property like position.x="5" and the X position gets set to 5.

My issue is value type fields can easily be modified with the code below as I can mutate the original value, but value type properties return a copy that will have to get modified and reassigned back to the source object. Any ideas on how to handle value type properties?

Example: position is a struct of type Vector2 that is a property. MyObject.position.x = 5 will not work because calling SetValue on position will create a temporary position and set x to 5 on it. Therefore, the actual position attached to the object will not get the value of 5. If I do MyObject.position = new Vector2(5,0) I will get the position set to 5,0 since I am not attempting to mutate the position elements and instead assigning an entire value to position.

Note: I wrote some extension methods so 'GetValue' is valid on member info.

private bool SetAttributeValue(string text,object obj,MemberInfo setter)
{
    BuildParseMethods();
    if (!TryParseAttribute(text, setter.GetMemberType(), out object value))
        return false;

    setter.SetValue(obj, value);

    return true;
}


private void SetChainAttribute(XmlAttribute attribute,object instance)
{
    object target = instance;
    MemberInfo[] bindings = null;
    string[] parts = attribute.Name.Split('.');

    Type type = instance.GetType();
    for (int i = 0; i < parts.Length; i++)
    {
        bindings = type.GetMember(parts[i], BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetField | BindingFlags.SetProperty | BindingFlags.IgnoreCase);

        if (bindings == null || bindings.Length == 0)
        {
            Debug.LogError($"Unable to find element {i} of {attribute.Name}");
            return;
        }

        if (i < parts.Length - 1)
        {
            target = bindings[0].GetValue(target);
            type = target != null ? target.GetType() : bindings[0].GetType();
        }
    }

    SetAttributeValue(attribute.Value, target, bindings[0]);
}




Aucun commentaire:

Enregistrer un commentaire