[英]How to create a Expression.Lambda when a type is not known until runtime?
最好使用代码解释。 我有一个泛型类,它有一个返回整数的方法。 这是一个简单的版本,用于解释......
public class Gen<T>
{
public int DoSomething(T instance)
{
// Real code does something more interesting!
return 1;
}
}
在运行时,我使用反射来发现某事物的类型,然后想要为该特定类型创建我的Gen类的实例。 这很容易,像这样完成......
Type fieldType = // This is the type I have discovered
Type genericType = typeof(Gen<>).MakeGenericType(fieldType);
object genericInstance = Activator.CreateInstance(genericType);
我现在想要创建一个Expression,它将参数作为泛型类型的一个实例,然后调用该类型的DoSomething方法。 所以我希望Expression能有效地执行此操作......
int answer = genericInstance.DoSomething(instance);
...除了我之前在运行时某点之后没有'实例',并且genericInstance是生成的类型,如上所示。 我为此创建Lambda的尝试如下......
MethodInfo mi = genericType.GetMethod("DoSomething",
BindingFlags.Instance | BindingFlags.Public);
var p1 = Expression.Parameter(genericType, "generic");
var p2 = Expression.Parameter(fieldType, "instance");
var x = Expression.Lambda<Func<genericType, fieldType, int>>
(Expression.Call(p1, mi, p2),
new[] { p1, p2 }).Compile();
......所以以后我可以用这样的东西来称它...
int answer = x(genericInstance, instance);
当然,您无法为Func提供实例参数,因此我不知道如何参数化Lambda生成。 有任何想法吗?
我想你会使用Expression.Lambda
,它将委托类型作为一个类型而不是泛型,并像使用Gen<>
一样动态创建你的Func:
MethodInfo mi = genericType.GetMethod("DoSomething",
BindingFlags.Instance | BindingFlags.Public);
var p1 = Expression.Parameter(genericType, "generic");
var p2 = Expression.Parameter(fieldType, "instance");
var func = typeof (Func<,,>);
var genericFunc = func.MakeGenericType(genericType, fieldType, typeof(int));
var x = Expression.Lambda(genericFunc, Expression.Call(p1, mi, p2),
new[] { p1, p2 }).Compile();
这将返回一个Delegate而不是一个强类型的Func
,但你当然可以在需要的时候抛出它(如果你不知道你要投射到什么,看起来很难),或者使用DynamicInvoke
动态调用它。
int answer = (int) x.DynamicInvoke(genericInstance, instance);
编辑 :
确实有效的好主意。 不幸的是,我想使用强类型编译的Lambda的原因是性能。 与类型化的Lambda相比,使用DynamicInvoke非常慢。
这似乎无需动态调用即可工作。
var p1 = Expression.Parameter(genericType, "generic");
var p2 = Expression.Parameter(fieldType, "instance");
var func = typeof(Func<,,>);
var genericFunc = func.MakeGenericType(genericType, fieldType, typeof(int));
var x = Expression.Lambda(genericFunc, Expression.Call(p1, mi, p2), new[] { p1, p2 });
var invoke = Expression.Invoke(x, Expression.Constant(genericInstance), Expression.Constant(instance));
var answer = Expression.Lambda<Func<int>>(invoke).Compile()();
编辑2 :
大大简化的版本:
Type fieldType = ;// This is the type I have discovered
Type genericType = typeof(Gen<>).MakeGenericType(fieldType);
object genericInstance = Activator.CreateInstance(genericType);
MethodInfo mi = genericType.GetMethod("DoSomething",
BindingFlags.Instance | BindingFlags.Public);
var value = Expression.Constant(instance, fieldType);
var lambda = Expression.Lambda<Func<int>>(Expression.Call(Expression.Constant(genericInstance), mi, value));
var answer = lambda.Compile()();
此答案仅适用于使用.NET 4.0的情况。
如果你使genericInstance
dynamic
而不是object
,你可以直接调用它上面的DoSomething
方法,动态语言运行库将为你处理一切。
class Type1 {
public int DoSomething() { return 1; }
}
class Type2 {
public int DoSomething() { return 2; }
}
static void TestDynamic() {
dynamic t1 = Activator.CreateInstance(typeof(Type1));
int answer1 = t1.DoSomething(); // returns 1
dynamic t2 = Activator.CreateInstance(typeof(Type2));
int answer2 = t2.DoSomething(); // returns 2
}
如果你需要保留这个类结构( Gen<T>
),那么我没有看到一个简单的方法,即在编译时你不知道类型T
如果要调用委托,则必须在编译时知道其完整类型,或者需要将参数作为对象传递。
使用dynamic
可以隐藏获取MethodInfo
等的复杂性,并为您提供出色的性能。 我看到的与DynamicInvoke
相比的一个缺点是,我相信你会得到为每个呼叫站点解析一次动态调用的初始开销。 如果在具有相同类型的对象上调用绑定,则会缓存绑定,以便它们从第二次开始运行得非常快。
最好接受一个object
并使用convert
为已知类型。
下面是一个示例,如何在未知深度上按名称构建对属性的访问:
var model = new { A = new { B = 10L } };
string prop = "A.B";
var parameter = Expression.Parameter(typeof(object));
Func<object, long> expr = (Func<object, long>) Expression.Lambda(prop.Split('.').Aggregate<string, Expression>(Expression.Convert(parameter, model.GetType()), Expression.Property), parameter).Compile();
expr(model).Dump();
当编译时未知委托类型时,它可以避免额外的DynamicInvoke成本。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.