最好用代码解释。我有一个泛型类,它有一个返回整数的方法。这是一个简单的版本,用于解释......
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生成。有什么想法吗?
答案 0 :(得分:19)
我认为您只需使用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();
这将返回一个委托而不是一个强类型的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()();
答案 1 :(得分:2)
此答案仅适用于使用.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
相比的一个缺点是,我相信你会得到为每个呼叫站点解决一次动态调用的初始开销。如果在相同类型的对象上调用绑定,则会缓存绑定,以便它们从第二次开始运行得非常快。
答案 2 :(得分:0)
最好接受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成本。