Frage

I am attempting to create an extension method which generates and stores compiled Lambda Expressions for, and returns an instance of, a type of the form Class of generic type T where both Class and T are only known at runtime (driven by user selection).

I am struggling to work out the correct syntax as I am new to Expression Trees.

The "makeGenericExpression" below gives the following error when run

"Static method requires null instance, non-static method requires non-null instance."

But it's entirely possible that makeGenericLambda is also flawed (I just haven't got to it yet)

Here's what I have so far (I'm using 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;

        }

    }
}

For completeness, the call I am using looks like this

    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);
    }
War es hilfreich?

Lösung

If I understand correctly what you want, you have two Types, let's call them T1<> and T2 and you want to create an instance of the type T1<T2>.

For that, you don't need any expression trees, just call MakeGenericType() directly and then use Activator.CreateInstance():

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

    return Activator.CreateInstance(genericType);
}

If Activator.CreateInstance() is too slow for you (and you should measure that it actually is too slow), then you could replace it with cached 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();
}

I decided to also avoid the call to MakeGenericType() when the type is already in cache, but I have no idea whether that would actually improve performance.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top