简体   繁体   English

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

[英]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.

相关问题 动态创建Expression.Lambda的通用类型 - Dynamically create generic type of an Expression.Lambda 如何创建类型为Expression.Lambda的Lambda表达式 <Action<Controller> &gt;? 动态地 - How do I create a lambda expression of type Expression.Lambda<Action<Controller>>? dynamically 如何创建programmaticaly Expression.Lambda <Func<TEntity, TProperty> &gt;带变量类型? - How Create programmaticaly Expression.Lambda<Func<TEntity, TProperty>> with variable Type? 如何将Expression.Lambda附加到任何所有者类型? - How to attach Expression.Lambda to ANY owner type? Expression.Lambda指定子类型的ParameterExpression - Expression.Lambda specify ParameterExpression of sub type 使用 Expression.Lambda 制作 lambda 表达式时如何使用变量<func<> &gt;() </func<> - How to use a variable when making a lambda expression using Expression.Lambda<Func<>>() Expression.Lambda和运行时的查询生成,最简单的“Where”示例 - Expression.Lambda and query generation at runtime, simplest “Where” example Expression.Lambda和运行时的查询生成,嵌套属性“Where”示例 - Expression.Lambda and query generation at runtime, nested property “Where” example C#Expression.Lambda无法在运行时编译 - C# Expression.Lambda cannot be compiled at runtime Expression.Lambda:变量&#39;&#39;&#39;类型&#39;&#39;引用范围&#39;&#39;,但它没有定义 - Expression.Lambda: Variable 'x' of type '' referenced from scope '', but it is not defined
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM