如何使用表达式树</t,>调用反射的Func <t,t =“”>属性

时间:2014-11-18 09:58:42

标签: c# linq-expressions

我有一个泛型类,其lambda属性定义如下:

public class Transformation<TProperty> : TransformationBase
{
    public Func<TProperty, TProperty> Transform { get; private set; }
    ...

我正在尝试编译一个可以调用此Transform属性的Action(在Foo的属性上)。我在编译时不了解TProperty。我从这开始:

    private static Action<Foo> Compile(Transformation transformation)
    {
        var fooParameter = Expression.Parameter(typeof(Foo));
        var changePropertyValue = Expression.Constant(transformation);
        var transformProperty = Expression.Property(changePropertyValue, "Transform");
        var transfromCall = Expression.Call(transformProperty, ?
    }

如何调用/执行transformProperty?

编辑:Foo(已知编译时)具有无类型属性值,需要使用Transformation的Transform属性进行转换:

    public class Foo {
         public object Value { get; set; }
    }

所以,手写为TProperty字符串的例子是:

    Foo foo = ... // coming from an external source
    Transformation<string> tranformation = ... // coming from an external source
    foo.Value = transformation.Transform((string)foo.Value);

除了我不知道转换的确切类型,因为它在外部程序集中定义。所以,而不是字符串,它可能是int或其他东西。这就是为什么我想使用表达式树为给定的转换编译Action,这样我就可以调用:

    Foo foo = ... // coming from an external source
    TransformationBase transformation = ... // coming from an external source
    Action<Foo> transform = Compile(transformation);
    transform(foo); // should transform foo.Value using the Transform property of 'transformation'

注意:我让Transformation继承自TransformationBase以澄清这个讨论。

3 个答案:

答案 0 :(得分:1)

您的问题更多地与您的问题缺乏打字有关。 Foo.Value是松散类型的,但您的转换函数是强类型的。表达树也是强类型的。使用它们不允许您以松散类型的方式神奇地调用代码。

解决方案要么是很多反思,要么是一些简单的动态:

编辑:我添加了使用ExpressionTrees的CompileUntyped。我还添加了CompileReflection,它使用不带ExpressionTrees的Reflection。我会推荐使用动态的那个。它是迄今为止最容易阅读的,因此最容易维护。

class Program
{
    static void Main(string[] args)
    {
        var testTransform = new Transformation<string>
            {
                Transform = s => s.ToUpper()
            };
        var a = Compile(testTransform);
        var foo = new Foo
            {
                Value = "test"
            };

        a(foo);

        //foo.Value is now TEST
    }

    public static Action<Foo> CompileReflection(TransformationBase transformation)
    {
        var f = transformation
            .GetType()
            .GetProperty("Transform")
            .GetGetMethod()
            .Invoke(transformation, null) as Delegate;

        return foo => foo.Value = f.DynamicInvoke(foo.Value);

    }

    public static Action<Foo> Compile(TransformationBase transformation)
    {
        return new Action<Foo>(f =>
            {
                dynamic d = f.Value;
                dynamic t = transformation;
                f.Value = t.Transform(d);
            });
    }

    public static Action<Foo> CompileUntyped(TransformationBase transformation)
    {
        var transformType = transformation.GetType();
        var genericType = transformType.GetGenericArguments().First();

        var fooParam = Expression.Parameter(typeof(Foo), "f");

        var valueGetter = typeof(Foo).GetProperty("Value").GetGetMethod();
        var valueSetter = typeof(Foo).GetProperty("Value").GetSetMethod();
        var transformFuncMember = transformType.GetProperty("Transform").GetGetMethod();

        //Equivalent to f => f.Value = transformation.Transform((T)f.Value)
        //Where T is the generic type parameter of the Transformation, and f is of type Foo
        var expression = Expression.Lambda<Action<Foo>>(
            Expression.Call(
                fooParam,
                valueSetter,
                Expression.Invoke(
                    Expression.Property(
                        Expression.Constant(transformation, transformType), 
                        transformFuncMember
                    ),
                    Expression.Convert(
                        Expression.Property(fooParam, valueGetter),
                        genericType
                    )
                )
            ), fooParam
        );
        return expression.Compile();
    }

}

public class TransformationBase { }

public class Transformation<TProperty> : TransformationBase
{
    public Func<TProperty, TProperty> Transform { get; set; }
}

public class Foo
{
    public object Value { get; set; }
}

答案 1 :(得分:0)

不确定你想要做什么但是如果我理解你的意图 - 我认为不需要编译Expressions

private static Action<TProperty> Compile<TProperty>(Transformation<TProperty> transformation)
{
    return new Action<TProperty>(p => transformation.Transform(p));
}

答案 2 :(得分:0)

看一个例子,它应该给你你想要的东西。

void Main()
{

    var dummyObject = new Dummy { Test = "Hello!" };

    var propertyTransform = Create(dummyObject, "Test");

    propertyTransform(dummyObject);

    Console.WriteLine("Final transformation " + dummyObject.Test);
}

class Dummy {
    public string Test { get; set; }
}

// Define other methods and classes here
public class Transformation<TProperty>
{
    public Func<TProperty, TProperty> Transform { get; set; }
}

public static Action<TObj> Create<TObj>(TObj myObject, string property){
    var prop = myObject
        .GetType()
        .GetProperty(property);

    var val = prop.GetValue(myObject);

    var transformation = Create((dynamic)val);
    var transform = transformation.Transform;

    return obj => {

        var newValue = transform((dynamic)val);

        prop.SetValue(myObject, newValue);
    };
}

public static Transformation<TProperty> Create<TProperty>(TProperty property){

    var transformation = new Transformation<TProperty>();

    // just a dummy hijacking.
    if(typeof(TProperty)==typeof(string)){

        Func<string, string> test = input => "I am changed man!";

        transformation.Transform = (dynamic)test;
    }

    return transformation;
}

输出:

  

Final transformation I am changed man!