为仅在运行时知道的类型创建已编译的Expession.Lambda

时间:2013-12-22 14:16:39

标签: c#-4.0 generics lambda expression-trees

我正在尝试创建一个扩展方法,该方法生成并存储已编译的Lambda表达式,并返回一个类型为泛型类型T的类型的实例,其中Class和T仅在运行时已知(由用户驱动)选择)。

我正在努力找出正确的语法,因为我是Expression Trees的新手。

下面的“makeGenericExpression”在运行时会出现以下错误

  

“静态方法需要null实例,非静态方法需要   非空实例。“

但makeGenericLambda完全有可能也存在缺陷(我还没有完成)

这是我到目前为止所拥有的(我正在使用C#4.0)

public static class TypeXtensions
{
    public static object GetGenericInstance<TArg>(this Type type, TArg argument)
    {
        return InstanceCreationFactory<TArg>
            .CreateGenericInstanceOf(type, argument);
    }

    private static class InstanceCreationFactory<TArg>
    {
        private static readonly Dictionary<Type, Func<TArg, object>> GenericInstanceCreationMethods =
            new Dictionary<Type, Func<TArg, object>>();

        public static object CreateGenericInstanceOf(Type type, TArg arg)
        {
            CacheGenericInstanceCreationMethodIfRequired(type);

            return GenericInstanceCreationMethods[type].Invoke(arg);
        }

        private static void CacheGenericInstanceCreationMethodIfRequired(Type type)
        {
            if (GenericInstanceCreationMethods.ContainsKey(type)) return;

            var makeGenericMI = type.GetType().GetMethod("MakeGenericType");
            var paramExpression = Expression.Parameter(typeof (TArg), "argument");

            var makeGenericExpression = Expression.Call(makeGenericMI, paramExpression);

            // Compile the Expression into a Func which takes the type argument and returns the constructed object:
            var makeGenericLambda = Expression
                .Lambda<Func<TArg, object>>(makeGenericExpression)
                .Compile();


            GenericInstanceCreationMethods[type] = makeGenericLambda;

        }

    }
}

为了完整性,我正在使用的电话看起来像这样

    private void InputDataChanged(object sender, InputConnector<IndicatorStrategy>.InputDataChangedEventArgs e)
    {
        var baseType = e.Payload.GetType().BaseType;
        if (baseType == null) return;
        var arg = baseType.GetGenericArguments()[0];
        var test = typeof (ComplexIndicatorDataGenerator<>).GetGenericInstance(arg);
    }

1 个答案:

答案 0 :(得分:3)

如果我理解你想要的是什么,你有两个Type,我们称之为T1<>T2,你想创建一个T1<T2>类型的实例

为此,您不需要任何表达式树,只需直接调用MakeGenericType()然后使用Activator.CreateInstance()

public static object CreateGenericInstance(
    Type genericTypeDefinition, Type genericParameter)
{
    var genericType = genericTypeDefinition.MakeGenericType(genericParameter);

    return Activator.CreateInstance(genericType);
}

如果Activator.CreateInstance()对你来说太慢了(并且你应该测量它实际上太慢了),那么你可以用缓存的lambdas替换它:

Dictionary<Tuple<Type, Type>, Func<object>> instanceCreatorCache
    = new  Dictionary<Tuple<Type, Type>, Func<object>>();

object CreateGenericInstance(Type genericTypeDefinition, Type genericParameter)
{
    Func<object> instanceCreator;

    var cacheKey = Tuple.Create(genericTypeDefinition, genericParameter);

    if (!instanceCreatorCache.TryGetValue(cacheKey, out instanceCreator))
    {
        var genericType = genericTypeDefinition.MakeGenericType(genericParameter);

        instanceCreator = Expression.Lambda<Func<object>>(
            Expression.New(genericType)).Compile();

        instanceCreatorCache[cacheKey] = instanceCreator;
    }

    return instanceCreator();
}

我决定在类型已经在缓存中时避免调用MakeGenericType(),但我不知道这是否会真正提高性能。