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

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

问题描述

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

  public class Gen< T> 
{
public int DoSomething(T instance)
{
//真正的代码更有趣!
返回1;




$ b $ p
$ b

在运行时,我使用反射来发现某种类型的东西然后想为这个特定类型创建我的Gen类的实例。这很容易做到这一点...

 输入fieldType = //这是我发现的类型
类型genericType = typeof(Gen<>)MakeGenericType(fieldType);
对象genericInstance = Activator.CreateInstance(genericType);

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

  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})。

这将返回一个委托,而不是强类型的 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

一个大大简化的版本:

 类型fieldType =; //这是我发现的类型
类型genericType = typeof(Gen>)。MakeGenericType(fieldType);
对象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 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. 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. So I want the Expression to effectively perform this...

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

Of course, you cannot provide Func with instance parameters and so I have no idea how to parameterize the Lambda generation. 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<>:

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.

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

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

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

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