如果直到运行时才知道类型,如何创建Expression.Lambda?

Phi*_*ght 14 c# generics lambda expression-trees

最好使用代码解释.我有一个泛型类,它有一个返回整数的方法.这是一个简单的版本,用于解释......

public class Gen<T>
{
    public int DoSomething(T instance)
    {
        // Real code does something more interesting!
        return 1;
    }
}
Run Code Online (Sandbox Code Playgroud)

在运行时,我使用反射来发现某事物的类型,然后想要为该特定类型创建我的Gen类的实例.这很容易,像这样完成......

Type fieldType = // This is the type I have discovered
Type genericType = typeof(Gen<>).MakeGenericType(fieldType);
object genericInstance = Activator.CreateInstance(genericType);
Run Code Online (Sandbox Code Playgroud)

我现在想要创建一个Expression,它将参数作为泛型类型的一个实例,然后调用该类型的DoSomething方法.所以我希望Expression能有效地执行此操作......

int answer = genericInstance.DoSomething(instance);
Run Code Online (Sandbox Code Playgroud)

...除了我之前在运行时某点之后没有'实例',并且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();
Run Code Online (Sandbox Code Playgroud)

......所以以后我可以用这样的东西来称它...

int answer = x(genericInstance, instance);
Run Code Online (Sandbox Code Playgroud)

当然,您无法为Func提供实例参数,因此我不知道如何参数化Lambda生成.有任何想法吗?

vcs*_*nes 20

我认为你只Expression.Lambda需要使用委托类型作为类型而不是通用,并像你一样随时创建你的Func Gen<>:

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();
Run Code Online (Sandbox Code Playgroud)

这将返回一个Delegate而不是一个强类型Func,但你当然可以在需要时抛出它(如果你不知道你要投射到什么,看起来很难),或者使用它来动态调用DynamicInvoke它.

int answer = (int) x.DynamicInvoke(genericInstance, instance);
Run Code Online (Sandbox Code Playgroud)

编辑:

确实有效的好主意.不幸的是,我想使用强类型编译的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()();
Run Code Online (Sandbox Code Playgroud)

编辑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()();
Run Code Online (Sandbox Code Playgroud)