快速创建对象而不是Activator.CreateInstance(type)

时间:2011-07-05 11:56:46

标签: c# reflection reflection.emit activator createinstance

我正在努力提高应用程序的性能。我们有很多Activator.CreateInstance调用引起了一些悲伤。

我们基于一个接口(ITabDocument)实例化了很多类,在浏览后我想到了使用这个代码:

与使用我们的Activator.CreateInstance代码相比,代码并不是更好(实际上速度稍慢)。

    public static Func<T> CreateInstance<T>(Type objType) where T : class, new()
    {
        var dynMethod = new DynamicMethod("DM$OBJ_FACTORY_" + objType.Name, objType, null, objType);
        ILGenerator ilGen = dynMethod.GetILGenerator();
        ilGen.Emit(OpCodes.Newobj, objType.GetConstructor(Type.EmptyTypes));
        ilGen.Emit(OpCodes.Ret);
        return (Func<T>)dynMethod.CreateDelegate(typeof(Func<T>));
    }

我想知道为什么会这样,我所做的只是:

ITabDocument document = CreateInstance<ITabDocument>(Type.GetType("[Company].Something"));

有没有更好的方法来创建有助于上述内容的对象?当你不确定具体的类型时,它有点难。

6 个答案:

答案 0 :(得分:45)

我在这些之间做了一些基准测试(我会写下最基本的细节):

public static T Instance() //~1800 ms
{
    return new T();
}

public static T Instance() //~1800 ms
{
    return new Activator.CreateInstance<T>();
}

public static readonly Func<T> Instance = () => new T(); //~1800 ms

public static readonly Func<T> Instance = () => 
                                 Activator.CreateInstance<T>(); //~1800 ms

//works for types with no default constructor as well
public static readonly Func<T> Instance = () => 
               (T)FormatterServices.GetUninitializedObject(typeof(T)); //~2000 ms


public static readonly Func<T> Instance = 
     Expression.Lambda<Func<T>>(Expression.New(typeof(T))).Compile();  
     //~50 ms for classes and ~100 ms for structs

正如CD所说,编译表达式是最快的,并且大幅度提升。除(T)FormatterServices.GetUninitializedObject(typeof(T)) 之外的所有方法仅适用于具有默认构造函数的类型。

当你有一个每个泛型类型的静态类时,缓存已编译的结果委托是微不足道的。像:

public static class New<T> where T : new()
{
    public static readonly Func<T> Instance = Expression.Lambda<Func<T>>
                                              (
                                               Expression.New(typeof(T))
                                              ).Compile();
}

请注意new约束。打电话给任何人

MyType me = New<MyType>.Instance();

除了第一次将类加载到内存中外,执行速度最快。

要有一个使用默认构造函数处理这两种类型的类,我采用混合方法,from here

public static class New<T>
{
    public static readonly Func<T> Instance = Creator();

    static Func<T> Creator()
    {
        Type t = typeof(T);
        if (t == typeof(string))
            return Expression.Lambda<Func<T>>(Expression.Constant(string.Empty)).Compile();

        if (t.HasDefaultConstructor())
            return Expression.Lambda<Func<T>>(Expression.New(t)).Compile();

        return () => (T)FormatterServices.GetUninitializedObject(t);
    }
}

public static bool HasDefaultConstructor(this Type t)
{
    return t.IsValueType || t.GetConstructor(Type.EmptyTypes) != null;
}

也会以有效的方式处理价值类型。

请注意,(T)FormatterServices.GetUninitializedObject(t) string将失败。因此,对字符串进行特殊处理以返回空字符串。

答案 1 :(得分:15)

这可能有所帮助:Don’t use Activator.CreateInstance or ConstructorInfo.Invoke, use compiled lambda expressions

// Make a NewExpression that calls the ctor with the args we just created
NewExpression newExp = Expression.New(ctor, argsExp);                  

// Create a lambda with the New expression as body and our param object[] as arg
LambdaExpression lambda = Expression.Lambda(typeof(ObjectActivator), newExp, param);            


// Compile it
ObjectActivator compiled = (ObjectActivator)lambda.Compile();

答案 2 :(得分:7)

问题是如果你要反复直接调用CreateInstance而不是将结果保存到某个地方并反复使用该结果,你可能应该继续并在内部进行缓存。

internal static class DelegateStore<T> {
     internal static IDictionary<string, Func<T>> Store = new ConcurrentDictionary<string,Func<T>>();
}

public static T CreateInstance<T>(Type objType) where T : class
{
    Func<T> returnFunc;
    if(!DelegateStore<T>.Store.TryGetValue(objType.FullName, out returnFunc)) {
        var dynMethod = new DynamicMethod("DM$OBJ_FACTORY_" + objType.Name, objType, null, objType);
        ILGenerator ilGen = dynMethod.GetILGenerator();
        ilGen.Emit(OpCodes.Newobj, objType.GetConstructor(Type.EmptyTypes));
        ilGen.Emit(OpCodes.Ret);
        returnFunc = (Func<T>)dynMethod.CreateDelegate(typeof(Func<T>));
        DelegateStore<T>.Store[objType.FullName] = returnFunc;
    }
    return returnFunc();
}

答案 3 :(得分:3)

您可能会从生成相同代码中获得一些开销。

ILGenerator动态创建工厂代码。

创建一些您已经使用过的地图或Dictionary类型,并保留为该类型创建的工厂方法。

答案 4 :(得分:2)

2019年12月13日
nawfal's answer benchmarked

以NetCore3.1为基准,只是看到它仍然有多么必要。

[MemoryDiagnoser, ThreadingDiagnoser]
[SimpleJob(runtimeMoniker: RuntimeMoniker.NetCoreApp31)]
[GenericTypeArguments(typeof(TestClass))]
public class ActivatorBenchmark<T> where T : new()
{
    [Benchmark(Baseline = true)]
    [Arguments(1_000)]
    [Arguments(1_000_000)]
    [Arguments(100_000_000)]

    public void ActivatorTest1(int x)
    {
        for (int i = 0; i < x; i++)
        {
            var t = new T();
        }
    }

    [Benchmark]
    [Arguments(1_000)]
    [Arguments(1_000_000)]
    [Arguments(100_000_000)]
    public void ActivatorTest2(int x)
    {
        for (int i = 0; i < x; i++)
        {
            var t = New<T>.Instance();
        }
    }
}

public class TestClass
{
    public string Name { get; set; }
    public int Id { get; set; }
    public string Email { get; set; }
}

public static class TestHelpers
{
    public static class New<T>
    {
        public static readonly Func<T> Instance = Creator();

        private static Func<T> Creator()
        {
            Type t = typeof(T);
            if (t == typeof(string))
                return Expression.Lambda<Func<T>>(Expression.Constant(string.Empty)).Compile();

            if (t.HasDefaultConstructor())
                return Expression.Lambda<Func<T>>(Expression.New(t)).Compile();

            return () => (T)FormatterServices.GetUninitializedObject(t);
        }
    }

    public static bool HasDefaultConstructor(this Type t)
    {
        return t.IsValueType || t.GetConstructor(Type.EmptyTypes) != null;
    }
}

// * Detailed results *
ActivatorBenchmark<TestClass>.ActivatorTest1: Job-RRTXQH(Runtime=.NET Core 3.1) [x=1000]
Runtime = .NET Core 3.1.0 (CoreCLR 4.700.19.56402, CoreFX 4.700.19.56404), X64 RyuJIT; GC = Concurrent Workstation
Mean = 30.9925 us, StdErr = 0.1962 us (0.63%); N = 17, StdDev = 0.8088 us
Min = 30.2190 us, Q1 = 30.4151 us, Median = 30.4745 us, Q3 = 31.5188 us, Max = 32.7203 us
IQR = 1.1037 us, LowerFence = 28.7595 us, UpperFence = 33.1744 us
ConfidenceInterval = [30.2049 us; 31.7802 us] (CI 99.9%), Margin = 0.7876 us (2.54% of Mean)
Skewness = 0.96, Kurtosis = 2.4, MValue = 2
-------------------- Histogram --------------------
[29.944 us ; 30.909 us) | @@@@@@@@@@@
[30.909 us ; 31.555 us) | @@
[31.555 us ; 32.301 us) | @@
[32.301 us ; 32.996 us) | @@
---------------------------------------------------

ActivatorBenchmark<TestClass>.ActivatorTest2: Job-RRTXQH(Runtime=.NET Core 3.1) [x=1000]
Runtime = .NET Core 3.1.0 (CoreCLR 4.700.19.56402, CoreFX 4.700.19.56404), X64 RyuJIT; GC = Concurrent Workstation
Mean = 7.0534 us, StdErr = 0.0144 us (0.20%); N = 14, StdDev = 0.0538 us
Min = 6.9228 us, Q1 = 7.0269 us, Median = 7.0687 us, Q3 = 7.0849 us, Max = 7.1367 us
IQR = 0.0580 us, LowerFence = 6.9399 us, UpperFence = 7.1718 us
ConfidenceInterval = [6.9927 us; 7.1142 us] (CI 99.9%), Margin = 0.0607 us (0.86% of Mean)
Skewness = -0.76, Kurtosis = 3.12, MValue = 2
-------------------- Histogram --------------------
[6.903 us ; 7.148 us) | @@@@@@@@@@@@@@
---------------------------------------------------

ActivatorBenchmark<TestClass>.ActivatorTest1: Job-RRTXQH(Runtime=.NET Core 3.1) [x=1000000]
Runtime = .NET Core 3.1.0 (CoreCLR 4.700.19.56402, CoreFX 4.700.19.56404), X64 RyuJIT; GC = Concurrent Workstation
Mean = 27.6912 ms, StdErr = 0.0412 ms (0.15%); N = 13, StdDev = 0.1486 ms
Min = 27.5318 ms, Q1 = 27.6171 ms, Median = 27.6435 ms, Q3 = 27.7196 ms, Max = 28.0946 ms
IQR = 0.1025 ms, LowerFence = 27.4633 ms, UpperFence = 27.8734 ms
ConfidenceInterval = [27.5132 ms; 27.8691 ms] (CI 99.9%), Margin = 0.1780 ms (0.64% of Mean)
Skewness = 1.56, Kurtosis = 4.57, MValue = 2
-------------------- Histogram --------------------
[27.477 ms ; 28.150 ms) | @@@@@@@@@@@@@
---------------------------------------------------

ActivatorBenchmark<TestClass>.ActivatorTest2: Job-RRTXQH(Runtime=.NET Core 3.1) [x=1000000]
Runtime = .NET Core 3.1.0 (CoreCLR 4.700.19.56402, CoreFX 4.700.19.56404), X64 RyuJIT; GC = Concurrent Workstation
Mean = 7.0697 ms, StdErr = 0.0239 ms (0.34%); N = 15, StdDev = 0.0927 ms
Min = 6.9195 ms, Q1 = 6.9889 ms, Median = 7.0681 ms, Q3 = 7.1187 ms, Max = 7.2497 ms
IQR = 0.1298 ms, LowerFence = 6.7942 ms, UpperFence = 7.3134 ms
ConfidenceInterval = [6.9706 ms; 7.1687 ms] (CI 99.9%), Margin = 0.0991 ms (1.40% of Mean)
Skewness = 0.22, Kurtosis = 2.08, MValue = 2
-------------------- Histogram --------------------
[6.887 ms ; 7.252 ms) | @@@@@@@@@@@@@@@
---------------------------------------------------

ActivatorBenchmark<TestClass>.ActivatorTest1: Job-RRTXQH(Runtime=.NET Core 3.1) [x=100000000]
Runtime = .NET Core 3.1.0 (CoreCLR 4.700.19.56402, CoreFX 4.700.19.56404), X64 RyuJIT; GC = Concurrent Workstation
Mean = 2.7852 s, StdErr = 0.0093 s (0.33%); N = 15, StdDev = 0.0358 s
Min = 2.7426 s, Q1 = 2.7576 s, Median = 2.7693 s, Q3 = 2.8141 s, Max = 2.8629 s
IQR = 0.0565 s, LowerFence = 2.6728 s, UpperFence = 2.8989 s
ConfidenceInterval = [2.7469 s; 2.8235 s] (CI 99.9%), Margin = 0.0383 s (1.38% of Mean)
Skewness = 0.76, Kurtosis = 2.21, MValue = 2
-------------------- Histogram --------------------
[2.735 s ; 2.796 s) | @@@@@@@@@@
[2.796 s ; 2.876 s) | @@@@@
---------------------------------------------------

ActivatorBenchmark<TestClass>.ActivatorTest2: Job-RRTXQH(Runtime=.NET Core 3.1) [x=100000000]
Runtime = .NET Core 3.1.0 (CoreCLR 4.700.19.56402, CoreFX 4.700.19.56404), X64 RyuJIT; GC = Concurrent Workstation
Mean = 698.4029 ms, StdErr = 1.3760 ms (0.20%); N = 15, StdDev = 5.3292 ms
Min = 688.2649 ms, Q1 = 693.8483 ms, Median = 697.9546 ms, Q3 = 703.2100 ms, Max = 707.1939 ms
IQR = 9.3617 ms, LowerFence = 679.8057 ms, UpperFence = 717.2526 ms
ConfidenceInterval = [692.7056 ms; 704.1001 ms] (CI 99.9%), Margin = 5.6972 ms (0.82% of Mean)
Skewness = 0.09, Kurtosis = 2.08, MValue = 2
-------------------- Histogram --------------------
[686.374 ms ; 709.085 ms) | @@@@@@@@@@@@@@@
---------------------------------------------------

// * Summary *

BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18362
Intel Core i9-9980HK CPU 2.40GHz, 1 CPU, 16 logical and 8 physical cores
.NET Core SDK=3.1.100
  [Host]     : .NET Core 3.1.0 (CoreCLR 4.700.19.56402, CoreFX 4.700.19.56404), X64 RyuJIT
  Job-RRTXQH : .NET Core 3.1.0 (CoreCLR 4.700.19.56402, CoreFX 4.700.19.56404), X64 RyuJIT

Runtime=.NET Core 3.1

|         Method |         x |             Mean |          Error |         StdDev | Ratio | Completed Work Items | Lock Contentions |       Gen 0 |     Gen 1 | Gen 2 |    Allocated |
|--------------- |---------- |-----------------:|---------------:|---------------:|------:|---------------------:|-----------------:|------------:|----------:|------:|-------------:|
| ActivatorTest1 |      1000 |        30.993 us |      0.7876 us |      0.8088 us |  1.00 |               0.0001 |                - |      4.7607 |         - |     - |     39.06 KB |
| ActivatorTest2 |      1000 |         7.053 us |      0.0607 us |      0.0538 us |  0.23 |               0.0000 |                - |      4.7760 |         - |     - |     39.06 KB |
|                |           |                  |                |                |       |                      |                  |             |           |       |              |
| ActivatorTest1 |   1000000 |    27,691.175 us |    177.9530 us |    148.5988 us |  1.00 |               0.0625 |                - |   4781.2500 |         - |     - |  39064.19 KB |
| ActivatorTest2 |   1000000 |     7,069.695 us |     99.0538 us |     92.6550 us |  0.26 |               0.0156 |                - |   4781.2500 |         - |     - |   39062.5 KB |
|                |           |                  |                |                |       |                      |                  |             |           |       |              |
| ActivatorTest1 | 100000000 | 2,785,181.093 us | 38,325.0183 us | 35,849.2459 us |  1.00 |               2.0000 |                - | 478000.0000 | 2000.0000 |     - | 3906418.4 KB |
| ActivatorTest2 | 100000000 |   698,402.860 us |  5,697.2360 us |  5,329.1981 us |  0.25 |               2.0000 |                - | 478000.0000 |         - |     - |   3906250 KB |

// * Hints *
Outliers
  ActivatorBenchmark<TestClass>.ActivatorTest2: Runtime=.NET Core 3.1 -> 1 outlier  was  removed, 2 outliers were detected (6.92 us, 9.73 us)
  ActivatorBenchmark<TestClass>.ActivatorTest1: Runtime=.NET Core 3.1 -> 2 outliers were removed (28.56 ms, 29.78 ms)

// * Legends *
  x                    : Value of the 'x' parameter
  Mean                 : Arithmetic mean of all measurements
  Error                : Half of 99.9% confidence interval
  StdDev               : Standard deviation of all measurements
  Ratio                : Mean of the ratio distribution ([Current]/[Baseline])
  Completed Work Items : The number of work items that have been processed in ThreadPool (per single operation)
  Lock Contentions     : The number of times there was contention upon trying to take a Monitor's lock (per single operation)
  Gen 0                : GC Generation 0 collects per 1000 operations
  Gen 1                : GC Generation 1 collects per 1000 operations
  Gen 2                : GC Generation 2 collects per 1000 operations
  Allocated            : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)
  1 us                 : 1 Microsecond (0.000001 sec)

// * Diagnostic Output - ThreadingDiagnoser *

// * Diagnostic Output - MemoryDiagnoser *

答案 5 :(得分:0)

构造委托的通用方法,直接调用构造函数。 使用给定委托类型的签名自动搜索给定类型的构造函数,并构造该类型的委托。代码在这里:

/// <summary>
/// Reflective object construction helper.
/// All methods are thread safe.
/// </summary>
public static class Constructor
{
    /// <summary>
    /// Searches an instanceType constructor with delegateType-matching signature and constructs delegate of delegateType creating new instance of instanceType.
    /// Instance is casted to delegateTypes's return type. 
    /// Delegate's return type must be assignable from instanceType.
    /// </summary>
    /// <param name="delegateType">Type of delegate, with constructor-corresponding signature to be constructed.</param>
    /// <param name="instanceType">Type of instance to be constructed.</param>
    /// <returns>Delegate of delegateType wich constructs instance of instanceType by calling corresponding instanceType constructor.</returns>
    public static Delegate Compile(Type delegateType,Type instanceType)
    {
        if (!typeof(Delegate).IsAssignableFrom(delegateType))
        {
            throw new ArgumentException(String.Format("{0} is not a Delegate type.",delegateType.FullName),"delegateType");
        }
        var invoke = delegateType.GetMethod("Invoke");
        var parameterTypes = invoke.GetParameters().Select(pi => pi.ParameterType).ToArray();
        var resultType = invoke.ReturnType;
        if(!resultType.IsAssignableFrom(instanceType))
        {
            throw new ArgumentException(String.Format("Delegate's return type ({0}) is not assignable from {1}.",resultType.FullName,instanceType.FullName));
        }
        var ctor = instanceType.GetConstructor(
            BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, parameterTypes, null);
        if(ctor == null)
        {
            throw new ArgumentException("Can't find constructor with delegate's signature","instanceType");
        }
        var parapeters = parameterTypes.Select(Expression.Parameter).ToArray();

        var newExpression = Expression.Lambda(delegateType,
            Expression.Convert(Expression.New(ctor, parapeters), resultType),
            parapeters);
        var @delegate = newExpression.Compile();
        return @delegate;
    }
    public static TDelegate Compile<TDelegate>(Type instanceType)
    {
        return (TDelegate) (object) Compile(typeof (TDelegate), instanceType);
    }
}

Yappi项目来源的一部分。使用它,您可以构造委托调用给定类型的任何构造函数,包括带参数的构造函数(ref和out参数除外)。

样本用法:

var newList = Constructor.Compile<Func<int, IList<String>>>(typeof (List<String>));
var list = newList(100);

构建委托后,将其存储在静态字典中或使用泛型参数的类的静态字段中。不要每次都构造新的委托。使用一个委托来构造给定类型的多个实例。