当直到运行时才知道类型时,如何创建 Expression.Lambda? [英] How to create a Expression.Lambda when a type is not known until runtime?

查看:20
本文介绍了当直到运行时才知道类型时,如何创建 Expression.Lambda?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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;
    }
}

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

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. 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);

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

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. So I want the Expression to effectively perform this...

int answer = genericInstance.DoSomething(instance);

...除了在运行时的某个时间点之前我没有实例",并且 genericInstance 是生成的类型,如上所示.我为此创建 Lambda 的尝试如下...

...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. My attempt at creating the Lambda for this is as follows...

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);

当然,你不能为 Func 提供实例参数,所以我不知道如何参数化 Lambda 代.有什么想法吗?

Of course, you cannot provide Func with instance parameters and so I have no idea how to parameterize the Lambda generation. Any ideas?

推荐答案

我认为你会使用 Expression.Lambda 将委托类型作为类型而不是泛型,然后创建您的 Func 就像使用 Gen<> 一样:

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<>:

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 调用它.

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.

int answer = (int) x.DynamicInvoke(genericInstance, instance);

编辑:

一个确实有效的好主意.不幸的是,我想使用强类型编译的 Lambda 的原因是性能.与类型化的 Lambda 相比,使用 DynamicInvoke 的速度非常慢.

A good idea that does indeed work. Unfortunately the reason I want to use a strongly typed compiled Lambda is performance. Using DynamicInvoke is prettty slow compared to a typed Lambda.

这似乎不需要动态调用就可以工作.

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()();

编辑 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()();

这篇关于当直到运行时才知道类型时,如何创建 Expression.Lambda?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆