简体   繁体   中英

How can I convert the following code to a Compiled Expression in C#?

I have a piece of code which is responsible for creating a generic list using reflection.

public static IList MakeList(Type listType)
{
    // checks to ensure listType is a generic list omitted...

    // gets the generic argument e.g. string for a List<string>
    var genericType = listType.GetGenericArguments()[0];

    return (IList)typeof(List<>)
        .MakeGenericType(genericType)
        .GetConstructor(Type.EmptyTypes)
        .Invoke(null);
}

Which I can invoke by:

var stringList = MakeList(typeof(IList<string>));
var intList = MakeList(typeof(IList<int>));

or for instance from a PropertyInfo :

MekList(propInfo.PropertyType);

Please note that since the type could come from the PrpertyInfo eg if the type of the property is IList<string> then using Activator.CreateInstance(propType) throws an exception of: Cannot create an instance of an interface.

How can I convert this to a compiled expression to benefit from the performance benefits?

You need a NewExpression . Example (I haven't tested):

private static Dictionary<Type, Func<IList>> funcs;
public static IList MakeList(Type listType)
{
    Func<IList> f;
    // checks to ensure listType is a generic list omitted...
    if(!funcs.TryGetValue(listType, out f)) {
        // gets the generic argument e.g. string for a List<string>
        var genericType = listType.GetGenericArguments()[0];

        var ctor = typeof(List<>)
                   .MakeGenericType(genericType)
                   .GetConstructor(Type.EmptyTypes);
        f = Expression.Lambda<Func<IList>>(
            Expression.New(ctor, new Expression[]{})).Compile();
        funcs[listType] = f;
    }
    return f();
}

Make sure you measure the performance to check that this really is faster than simply using Activator.CreateInstance.

Thanks to Random832 I ended up with:

public static class New
{
    public static readonly Func<Type, IList> MakeList = t => MakeListImpl(t)();

    private static readonly IDictionary<Type, Func<IList>> Cache = new Dictionary<Type, Func<IList>>();
    private static Func<IList> MakeListImpl(Type listType)
    {
        Func<IList> result;
        if (!Cache.TryGetValue(listType, out result))
        {
            Ensure.That(listType.IsGenericList());
            var genericArg = listType.GetGenericArguments()[0];
            var concreteType = typeof(List<>).MakeGenericType(genericArg);

            result = Expression.Lambda<Func<IList>>(Expression.New(concreteType)).Compile();
            Cache[listType] = result;
        }

        return result;
    }
}

Benchmarks (Release build | avg 1000 runs)

1,000,000
------------------------------------
Native:             00:00:00.0103980
ActivatorGeneric:   00:00:00.1274370
ActivatorType:      00:00:00.1115370
CompiledNew:        00:00:00.0261892

10,000,000
------------------------------------
Native:             00:00:00.1040899
ActivatorGeneric:   00:00:01.2864721
ActivatorType:      00:00:01.1627026
CompiledNew:        00:00:00.2432613

NewListNative           => list = new List<string>()
NewListActivatorGeneric => list = System.Activator.CreateInstance<List<string>>()
NewListActivatorType    => list = (List<string>) System.Activator.CreateInstance(typeof(List<string>))
CompiledNew             => list = (List<string>)New.MakeList(typeof(List<string>))

If you always use typeof(...) , you can also use a generic method like this:

public static IList<T> MakeList<T>()
{
    return new List<T>();
}   

You use it this way:

var stringList = MakeList<string>();
var intList = MakeList<int>();

If you don't know the type at compile time, you can not write it the way you want. You can call the same methods it your code snippet with Expression, but it will be the same. Try with Activator.CreateInstance(typeof(WhatEver)) .


I tested your code and Activator class CreateInstace method 10 thousand instances 100 times and the results are:

  1. For MakeList: 13 miliseconds
  2. For CreateInstance 0.8 miliseconds

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM