在 .Net 3.5 中使用相同的签名实现 Expression.Assign

本文关键字:实现 Assign Expression Net | 更新日期: 2023-09-27 18:30:56

Expression.Assign

.NET 4 之前不可用。我需要在此方法的 .NET 3.5 下使用原始签名实现:

public static BinaryExpression Assign(
    Expression left,
    Expression right
)

澄清:我不是在寻找在某些情况下使用此方法的方法。我需要实现一般情况。

在 .Net 3.5 中使用相同的签名实现 Expression.Assign

由于没有ExpressionType.Assign,因此无法获得完全相同的等价物,但可以制作类似的东西:


public static class ExpressionEx
{
    public static BinaryExpression Assign(Expression left, Expression right)
    {
        var assign = typeof(Assigner<>).MakeGenericType(left.Type).GetMethod("Assign");
        var assignExpr = Expression.Add(left, right, assign);
        return assignExpr;
    }
    private static class Assigner<T>
    {
        public static T Assign(ref T left, T right)
        {
            return (left = right);
        }
    }
}

然后,您可以使用它来生成分配:


class Foo
{
    public int Data
    {
        get;
        set;
    }
}
class Program
{
    static Action<object, object> MakeSetter(PropertyInfo info)
    {
        var objectParameter = Expression.Parameter(typeof(object), string.Empty);
        var valueParameter = Expression.Parameter(typeof(object), string.Empty);
        var setterExpression = Expression.Lambda<Action<object, object>>(
            ExpressionEx.Assign(
                Expression.Property(
                    Expression.Convert(objectParameter, info.DeclaringType),
                    info),
                Expression.Convert(valueParameter, info.PropertyType)),
            objectParameter,
            valueParameter);
        return setterExpression.Compile();
    }
    static void Main()
    {
        var foo = new Foo();
        var property = typeof(Foo).GetProperty("Data");
        var setter = MakeSetter(property);
        setter(foo, 10);
        Console.WriteLine(foo.Data);
    }
}

如果你真的不需要BinaryExpression作为返回类型,你可以使用Expression.Call而不是Add这样它就不会那么黑客化了。

恐怕你不能。

原因如下:

  1. ExpressionType枚举不包含 3.5 中的Assign成员。
  2. 我尝试使用错误的 ExpressionType 并使用 Expression.MakeBinary() 与我的方法创建一个二进制表达式,但该方法被忽略了。(生成的二进制表达式具有标准MethodMethod = null。
  3. 您不能在实例中分配Method - 它是只读的,并且类是密封的,因此您无法在派生类中解决它。

但是由于他们已经包含了MakeBinary() method参数,因此可能仍然存在一些解决方法......我不是100%确定这是不可能的。

基于康斯坦丁·奥兹诺比欣(Konstantin Oznobihin)的分支,这里有一个稍微修改过的,其中包含MethodCallExpression而不是BinaryExpression

public class ExpressionEx
{
    public static Expression Assign(Expression left, Expression right)
    {
        var method = typeof(ExpressionEx).GetMethod("Assign", BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(left.Type);
        return Expression.Call(method, left, right);
    }
    private static void Assign<T>(ref T left, T right)
    {
        left = right;
    }
}

这将在ExpressionEx中创建 Assign -方法的委托,现在可以像这样轻松使用:

var expr = ExpressionEx.Assign(myInstanceExpression, newValueExpression);