tags:

views:

247

answers:

3

I'm trying to use Expression.Dynamic() to build an assignment operation... I want to use this to selectively offer value type semantics to certain custom type instances in my language. I can't do this with a "static" (?) Expression because I don't know what the actual type is (I need the MetaObject instance and its LimitType... hence Expression.Dynamic() ).

This isn't working for me... Expression.Assign() does nothing if used to build a MetaObject from my OperationBinder subclass.

Head. Pounding. On. Desk. For. Hours.

Just wondering if this is a supported behavior, or if I'm barking up the wrong tree?

Thanks...

A: 

Can you clarify the exact scenario? I'm very familiar with Expression, and passably familiar with using it with DLR trees / 4.0, but I'm not 100% sure what you're trying to do. Note also that there are some limitations anyway with this type of usage (whether it is C# or raw trees).

Marc Gravell
+3  A: 

I think you basically have 2 options. In either one though you should be using a custom binder instead of a subclass of OperationBinder. This is because you're not performing a cross-language operation. Instead you're implementing a part of your language semantics but just want the good DLR stuff. You should sub class MetaObjectBinder to make this happen (MetaAction in older builds).

So your two choices then are to either have an Ast.Dynamic that returns the new value that you assign into a local or you pass the value as a ref argument. These should look like:

Ast.Assign(localVal, Ast.Dynamic(new AssignBinder(...), localVal, newVal);

or

delegate void AssignDelegate<TLocal, TValue>(CallSite site, TLocal loc, TValue val);
Type dlgType = typeof(AssignDelegate).MakeGenericType(new Type[] { localVal.Type, newVal.Type });
Ast.Dynamic(dlgType, new AssignBinder(...), localVal, newVal);

In your binder you'll override the Bind method which will give you the incoming MetaObject's. For the 1st one you'll just return the new value and the second one you'll just assign to the 1st MetaObject.

I haven't tried this but that's basically how it should work.

A: 

If you want to create an assignment expression, simply create an expression that invokes a generic assignment routine. The class below provides a nice extension method that will convert any field access expression into a field assignment expression:

using System;
using System.Linq.Expressions;
using System.Reflection;

namespace Ethica.Expressions
{
    public static class ExpressionExtenstions
    {
        private class AssignmentHelper<T>
        {
            private static void SetValue(ref T target, T value)
            {
                target = value;
            }

            internal static MethodInfo MethodInfoSetValue =
                typeof(AssignmentHelper<T>).GetMethod("SetValue", BindingFlags.NonPublic | BindingFlags.Static);
        }


        public static Expression<Action<TInstance, TProp>> ToFieldAssignExpression<TInstance, TProp>
            (
                this Expression<Func<TInstance, TProp>> fieldGetter
            )
        {
            if (fieldGetter == null)
                throw new ArgumentNullException("fieldGetter");

            if(fieldGetter.Parameters.Count != 1 || !(fieldGetter.Body is MemberExpression))
                throw new ArgumentException ("Input expression must be a single parameter field getter, e.g. g => g._fieldToSet  or function(g) g._fieldToSet");

            ParameterExpression[] parms = new ParameterExpression[] {
                                        fieldGetter.Parameters[0], 
                                        Expression.Parameter(typeof(TProp), "value")};

            Expression body = Expression.Call(AssignmentHelper<TProp>.MethodInfoSetValue,
                                              new Expression[] { fieldGetter.Body, parms[1] });

            return Expression.Lambda<Action<TInstance, TProp>>(body, parms);
        }


        public static Action<TInstance, TProp> ToFieldAssignment<TInstance, TProp>
            (
                this Expression<Func<TInstance, TProp>> fieldGetter
            )
        {
            return fieldGetter.ToFieldAssignExpression().Compile();
        }
    }
}
Mark