Assignment in .NET 3.5 expression trees

后端 未结 5 914
执笔经年
执笔经年 2020-12-16 01:39

Is it possible to encode an assignment into an expression tree?

相关标签:
5条回答
  • 2020-12-16 01:48

    My extension method for doing exactly this:

    /// <summary>
    /// Provides extensions for converting lambda functions into assignment actions
    /// </summary>
    public static class ExpressionExtenstions
    {
        /// <summary>
        /// Converts a field/property retrieve expression into a field/property assign expression
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <typeparam name="TProp">The type of the prop.</typeparam>
        /// <param name="fieldGetter">The field getter.</param>
        /// <returns></returns>
        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");
    
            var parms = new[]
                            {
                                fieldGetter.Parameters[0],
                                Expression.Parameter(typeof (TProp), "value")
                            };
    
            Expression body = Expression.Call(AssignmentHelper<TProp>.MethodInfoSetValue,
                                              new[] {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();
        }
    
        #region Nested type: AssignmentHelper
    
        private class AssignmentHelper<T>
        {
            internal static readonly MethodInfo MethodInfoSetValue =
                typeof (AssignmentHelper<T>).GetMethod("SetValue", BindingFlags.NonPublic | BindingFlags.Static);
    
            private static void SetValue(ref T target, T value)
            {
                target = value;
            }
        }
    
        #endregion
    }
    
    0 讨论(0)
  • 2020-12-16 01:50

    As Jon Skeet and TraumaPony have already said, Expression.Assign isn't available before .NET 4. Here's another concrete example of how to work around this missing bit:

    public static class AssignmentExpression
    {
        public static Expression Create(Expression left, Expression right)
        {
            return
                Expression.Call(
                   null,
                   typeof(AssignmentExpression)
                      .GetMethod("AssignTo", BindingFlags.NonPublic | BindingFlags.Static)
                      .MakeGenericMethod(left.Type),
                   left,
                   right);
        }
    
        private static void AssignTo<T>(ref T left, T right)  // note the 'ref', which is
        {                                                     // important when assigning
            left = right;                                     // to value types!
        }
    }
    

    Then simply call AssignmentExpression.Create() in place of Expression.Assign().

    0 讨论(0)
  • 2020-12-16 01:51

    You should able to do it with .NET 4.0 Library. by import Microsoft.Scripting.Core.dll to your .NET 3.5 project.

    I am using DLR 0.9 - There might be some change on Expession.Block and Expression.Scope in version 1.0 (You can see reference from http://www.codeplex.com/dlr/Thread/View.aspx?ThreadId=43234)

    Following sample is to show you.

    using System;
    using System.Collections.Generic;
    using Microsoft.Scripting.Ast;
    using Microsoft.Linq.Expressions;
    using System.Reflection;
    
    namespace dlr_sample
    {
        class Program
        {
            static void Main(string[] args)
            {
                List<Expression> statements = new List<Expression>();
    
                ParameterExpression x = Expression.Variable(typeof(int), "r");
                ParameterExpression y = Expression.Variable(typeof(int), "y");
    
                statements.Add(
                    Expression.Assign(
                        x,
                        Expression.Constant(1)
                    )
                 );
    
                statements.Add(
                    Expression.Assign(
                        y,
                        x
                    )
                 );
    
                MethodInfo cw = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) });
    
                statements.Add(
                    Expression.Call(
                        cw,
                        y
                    )
                );
    
                LambdaExpression lambda = Expression.Lambda(Expression.Scope(Expression.Block(statements), x, y));
    
                lambda.Compile().DynamicInvoke();
                Console.ReadLine();
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-16 01:53

    You could probably work around it by nexting expression trees. Call a lambda function, where an argument is the value of the assignee.

    0 讨论(0)
  • 2020-12-16 02:06

    No, I don't believe so.

    Certainly the C# compiler disallows it when converting a lambda expression:

    int x;
    Expression<Func<int,int>> foo = (x=y); // Assign to x and return value
    

    This yields the error:

    CS0832: An expression tree may not contain an assignment operator
    
    0 讨论(0)
提交回复
热议问题