簡體   English   中英

如何將以下代碼轉換為C#中的編譯表達式?

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

我有一段代碼負責使用反射創建通用列表。

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);
}

我可以通過以下方式調用:

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

或例如來自PropertyInfo

MekList(propInfo.PropertyType);

請注意,由於類型可能來自PrpertyInfo例如,如果屬性的類型為IList<string>則使用Activator.CreateInstance(propType)會引發以下異常: Cannot create an instance of an interface.

如何將其轉換為編譯表達式以從性能收益中受益?

您需要一個NewExpression 示例(我尚未測試):

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();
}

確保測量性能以檢查它確實比僅使用Activator.CreateInstance更快。

多虧了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;
    }
}

基准 (發布版本|平均運行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>))

如果您始終使用typeof(...) ,則還可以使用如下通用方法:

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

您可以通過以下方式使用它:

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

如果您在編譯時不知道類型,則無法以所需的方式編寫它。 您可以使用Expression調用與代碼段相同的方法,但方法相同。 嘗試使用Activator.CreateInstance(typeof(WhatEver))


我測試了您的代碼和Activator類的CreateInstace方法1萬次實例100次,結果是:

  1. 對於MakeList:13毫秒
  2. 對於CreateInstance 0.8毫秒

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM