[英]How to create a Expression.Lambda when a type is not known until runtime?
This is best explained using code. 最好使用代码解释。 I have a generic class that has a method that returns an integer.
我有一个泛型类,它有一个返回整数的方法。 Here is a simple version for the purposes of explaining...
这是一个简单的版本,用于解释......
public class Gen<T>
{
public int DoSomething(T instance)
{
// Real code does something more interesting!
return 1;
}
}
At runtime I use reflection to discover the type of something and then want to create an instance of my Gen class for that specific type. 在运行时,我使用反射来发现某事物的类型,然后想要为该特定类型创建我的Gen类的实例。 That is easy enough and done like this...
这很容易,像这样完成......
Type fieldType = // This is the type I have discovered
Type genericType = typeof(Gen<>).MakeGenericType(fieldType);
object genericInstance = Activator.CreateInstance(genericType);
I now want to create an Expression that will take as a parameter an instance of the generic type and then calls the DoSomething method of that type. 我现在想要创建一个Expression,它将参数作为泛型类型的一个实例,然后调用该类型的DoSomething方法。 So I want the Expression to effectively perform this...
所以我希望Expression能有效地执行此操作......
int answer = genericInstance.DoSomething(instance);
...except I do not have the 'instance' until some point later at runtime and the genericInstance is the generated type as can be seen above. ...除了我之前在运行时某点之后没有'实例',并且genericInstance是生成的类型,如上所示。 My attempt at creating the Lambda for this is as follows...
我为此创建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();
...so that later on I can call it with something like this... ......所以以后我可以用这样的东西来称它...
int answer = x(genericInstance, instance);
Of course, you cannot provide Func with instance parameters and so I have no idea how to parameterize the Lambda generation. 当然,您无法为Func提供实例参数,因此我不知道如何参数化Lambda生成。 Any ideas?
有任何想法吗?
I think you would just use the Expression.Lambda
that takes the delegate type as a type rather then as a generic, and create your Func on the fly like you are with Gen<>
: 我想你会使用
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();
This will return a Delegate rather than a strongly typed Func
, but you can of course cast it if needed (and seemingly difficult if you don't know what you are casting to), or dynamically invoke it using DynamicInvoke
on it. 这将返回一个Delegate而不是一个强类型的
Func
,但你当然可以在需要的时候抛出它(如果你不知道你要投射到什么,看起来很难),或者使用DynamicInvoke
动态调用它。
int answer = (int) x.DynamicInvoke(genericInstance, instance);
EDIT : 编辑 :
A good idea that does indeed work.
确实有效的好主意。 Unfortunately the reason I want to use a strongly typed compiled Lambda is performance.
不幸的是,我想使用强类型编译的Lambda的原因是性能。 Using DynamicInvoke is prettty slow compared to a typed Lambda.
与类型化的Lambda相比,使用DynamicInvoke非常慢。
This seems to work without the need of a dynamic invoke. 这似乎无需动态调用即可工作。
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()();
EDIT 2 : 编辑2 :
A greatly simplified version: 大大简化的版本:
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()();
This answer only applies if you are using .NET 4.0. 此答案仅适用于使用.NET 4.0的情况。
If you make genericInstance
dynamic
instead of object
, you can then call the DoSomething
method on it directly, and the dynamic language runtime will take care of everything for you. 如果你使
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
}
If you need to keep this class structure ( Gen<T>
), then I don't see an easy way around the fact that you don't know the type T
at compile time. 如果你需要保留这个类结构(
Gen<T>
),那么我没有看到一个简单的方法,即在编译时你不知道类型T
If you want to call the delegate, you either have to know its full type at compile time, or you need to pass in the parameters as objects. 如果要调用委托,则必须在编译时知道其完整类型,或者需要将参数作为对象传递。
Using dynamic
gets you to hide the complexity of getting the MethodInfo
, etc., and gives you excellent performance. 使用
dynamic
可以隐藏获取MethodInfo
等的复杂性,并为您提供出色的性能。 The one drawback vs. DynamicInvoke
that I see is that I believe you get the initial overhead of resolving the dynamic call once for every call site. 我看到的与
DynamicInvoke
相比的一个缺点是,我相信你会得到为每个呼叫站点解析一次动态调用的初始开销。 The bindings are cached so that they run very fast from the second time onwards if you call it on objects with the same type. 如果在具有相同类型的对象上调用绑定,则会缓存绑定,以便它们从第二次开始运行得非常快。
It's better to to accept an object
and use convert
to a known type. 最好接受一个
object
并使用convert
为已知类型。
Here is an example, how to build access to a property by name on unknown depth: 下面是一个示例,如何在未知深度上按名称构建对属性的访问:
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();
It avoids extra costs of DynamicInvoke when type of delegate is unknown at compile time. 当编译时未知委托类型时,它可以避免额外的DynamicInvoke成本。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.