简体   繁体   English

如何将以下代码转换为C#中的编译表达式?

[英]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 : 或例如来自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. 请注意,由于类型可能来自PrpertyInfo例如,如果属性的类型为IList<string>则使用Activator.CreateInstance(propType)会引发以下异常: 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 . 您需要一个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. 确保测量性能以检查它确实比仅使用Activator.CreateInstance更快。

Thanks to Random832 I ended up with: 多亏了Random832我最终得到了:

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) 基准 (发布版本|平均运行1000次)

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: 如果您始终使用typeof(...) ,则还可以使用如下通用方法:

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. 您可以使用Expression调用与代码段相同的方法,但方法相同。 Try with Activator.CreateInstance(typeof(WhatEver)) . 尝试使用Activator.CreateInstance(typeof(WhatEver))


I tested your code and Activator class CreateInstace method 10 thousand instances 100 times and the results are: 我测试了您的代码和Activator类的CreateInstace方法1万次实例100次,结果是:

  1. For MakeList: 13 miliseconds 对于MakeList:13毫秒
  2. For CreateInstance 0.8 miliseconds 对于CreateInstance 0.8毫秒

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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