我有一个泛型类,其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以澄清这个讨论。
答案 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!