Field Setter/Getter using Delegate

c# delegates expression-trees field mono

Question

This is my simple test code. I want to create field assignment link between 2 objects, and the field is determined at runtime using reflection by caching delegate of its setter/getter method. But, somehow it doesn't work. The assignment is not working; maybe I made a silly mistake. Where am I am wrong?

public static class AssignmentExpression
{
    public static Expression Create(Expression left, Expression right)
    {
        MethodInfo m = typeof(AssignmentExpression)
                  .GetMethod("AssignTo", BindingFlags.NonPublic | BindingFlags.Static)
                  .MakeGenericMethod(left.Type);

        return Expression.Call( null,m,left, right);
    }

    private static void AssignTo<T>(ref T left, T right)  
    {                                                     
        left = right;                                     
    }
}

public class FieldLink
{
    protected Delegate srcGetter;
    protected Delegate dstSetter;

    public FieldLink(FieldInfo srcObject, FieldInfo dstObject)
    {
        this.srcGetter = FieldLink.createGetter(srcObject);
        this.dstSetter = FieldLink.createSetter(dstObject);
    }

    public void update<T>(T dst, T src)
    {
        this.dstSetter.DynamicInvoke(dst, this.srcGetter.DynamicInvoke(src));
    }

    protected static Delegate createGetter(FieldInfo field)
    {
        ParameterExpression objParm = Expression.Parameter(field.DeclaringType, "obj");
        Type delegateType = typeof(Func<,>).MakeGenericType(field.DeclaringType, field.FieldType);
        MemberExpression fieldExpr = Expression.Field(objParm, field.Name);
        LambdaExpression lambda = Expression.Lambda(delegateType, fieldExpr, objParm);
        return lambda.Compile();
    }

    protected static Delegate createSetter(FieldInfo field)
    {
        ParameterExpression objParm = Expression.Parameter(field.DeclaringType, "obj");
        ParameterExpression valueParm = Expression.Parameter(field.FieldType, "value");
        Type delegateType = typeof(Action<,>).MakeGenericType(field.DeclaringType, field.FieldType);
        MemberExpression memberExpr = Expression.Field(objParm, field.Name);
        Expression assignExpr = AssignmentExpression.Create(memberExpr, valueParm);
        LambdaExpression lambda = Expression.Lambda(delegateType, assignExpr, objParm, valueParm);
        return lambda.Compile();
    }
}

public class Test
{
    public int fieldInt = 0;
}

public class TestClass
{
    public Test a = new Test();
    public Test b = new Test();
    public void Start()
    {
        a.fieldInt = 5;

        Debug.Log("before a = " + a.fieldInt + " b = " + b.fieldInt);
        FieldLink testLink = new FieldLink(this.a.GetType().GetField("fieldInt"),
                                           this.b.GetType().GetField("fieldInt"));
        testLink.update(this.b, this.a);
        Debug.Log("after  a = " + a.fieldInt + " b = " + b.fieldInt);
        //here a.fieldInt should be equal to b.fieldInt, but somehow its unchanged!
    }
}

Accepted Answer

Your code appears to work, but maybe not exactly how you're expecting. When you call update you pass in b.fieldInt as the left argument and a.fieldInt as the right argument; the update method then assigns the value of a.fieldInt (5) to the b.fieldInt field, resulting in both objects having a fieldInt value of 5. If you reverse the arguments, both fields end up as zero. Isn't that what you expect?

As an aside - and maybe you have other reasons for using your own AssignmentExpression class - but you can actually replace:

Expression assignExpr = AssignmentExpression.Create(memberExpr, valueParm);

...with:

Expression assignExpr = Expression.Assign(memberExpr, valueParm);

...and you get the same result.



Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why