How to create a Expression.Lambda when a type is not known until runtime?

后端 未结 3 1937
南方客
南方客 2020-12-14 10:19

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



        
3条回答
  •  不知归路
    2020-12-14 10:40

    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>(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>(Expression.Call(Expression.Constant(genericInstance), mi, value));
    var answer = lambda.Compile()();
    

提交回复
热议问题