System.Activator.CreateInstance(T)
方法是否存在性能问题(因为我怀疑它使用反射)大到足以阻止我们随便使用它?
答案 0 :(得分:49)
与往常一样,回答关于性能的问题的唯一正确方法是实际测量代码。
以下是测试的示例LINQPad程序:
与往常一样,使用性能程序时,可能存在一些错误导致结果出现偏差。
输出(时间值以毫秒为单位):
Test1 - Activator.CreateInstance<T>() 12342 Test2 - new T() 1119 Test3 - Delegate 1530 Baseline 578
请注意,上述时间适用于对象的100.000.000(1亿)个构造。开销可能不是您的程序的真正问题。
警告性结论是Activator.CreateInstance<T>
花费大约11倍的时间来完成与new T()
相同的工作,而代表大约需要1.5倍的时间。请注意,此处的构造函数不执行任何操作,因此我只尝试测量不同方法的开销。
编辑:我添加了一个不构造对象的基线调用,但是完成了其余的事情,并将其计时。以此为基准,看起来委托比简单的new()花费的时间多75%,而Activator.CreateInstance则需要大约1100%的时间。
然而,这是微优化。如果你真的需要这样做,并且找出一些时间关键代码的最后一次性能,我会手动编写一个委托代替使用,或者如果那是不可能的,即。你需要在运行时提供类型,我会使用Reflection.Emit动态生成该委托。
无论如何,这是我的真实答案:
如果您遇到性能问题,请先测量一下您的瓶颈所在。是的,上面的时间可能表明Activator.CreateInstance比动态构建的委托有更多的开销,但是在你获得(甚至不得不)达到这个优化级别之前,你的代码库可能会有更大的鱼。强>
只是为了确保我真的回答你的具体问题:不,我不会阻止使用Activator.CreateInstance。您应该知道它使用反射,以便您知道如果这会超出您的瓶颈分析列表,那么您可以对它做一些事情,但它使用反射的事实并不意味着它是瓶颈。
该计划:
void Main()
{
const int IterationCount = 100000000;
// warmup
Test1();
Test2();
Test3();
Test4();
// profile Activator.CreateInstance<T>()
Stopwatch sw = Stopwatch.StartNew();
for (int index = 0; index < IterationCount; index++)
Test1();
sw.Stop();
sw.ElapsedMilliseconds.Dump("Test1 - Activator.CreateInstance<T>()");
// profile new T()
sw.Restart();
for (int index = 0; index < IterationCount; index++)
Test2();
sw.Stop();
sw.ElapsedMilliseconds.Dump("Test2 - new T()");
// profile Delegate
sw.Restart();
for (int index = 0; index < IterationCount; index++)
Test3();
sw.Stop();
sw.ElapsedMilliseconds.Dump("Test3 - Delegate");
// profile Baseline
sw.Restart();
for (int index = 0; index < IterationCount; index++)
Test4();
sw.Stop();
sw.ElapsedMilliseconds.Dump("Baseline");
}
public void Test1()
{
var obj = Activator.CreateInstance<TestClass>();
GC.KeepAlive(obj);
}
public void Test2()
{
var obj = new TestClass();
GC.KeepAlive(obj);
}
static Func<TestClass> Create = delegate
{
return new TestClass();
};
public void Test3()
{
var obj = Create();
GC.KeepAlive(obj);
}
TestClass x = new TestClass();
public void Test4()
{
GC.KeepAlive(x);
}
public class TestClass
{
}
答案 1 :(得分:9)
以下是测试的示例C#.NET 4.0程序:
输出(时间值以毫秒为单位,来自2014年x86发布版本的强劲机器):
Test1 - Activator.CreateInstance<T>(): 8542
Test2 - new T() 1082
Test3 - Delegate 1214
Test4 - Generic new() 8759
Test5 - Generic activator 9166
Test6 - Generic activator with bindings 60772
Baseline 322
这是从Lasse V. Karlsen的回答中采用的,但重要的是包括泛型。请注意,指定绑定会使使用泛型减慢Activator的速度超过6倍!
using System;
using System.Reflection;
using System.Diagnostics;
namespace ConsoleApplication1
{
public class TestClass
{
}
class Program
{
static void Main(string[] args)
{
const int IterationCount = 100000000;
// warmup
Test1();
Test2();
Test3();
Test4<TestClass>();
Test5<TestClass>();
Test6<TestClass>();
// profile Activator.CreateInstance<T>()
Stopwatch sw = Stopwatch.StartNew();
for (int index = 0; index < IterationCount; index++)
Test1();
sw.Stop();
Console.WriteLine("Test1 - Activator.CreateInstance<T>(): {0}", sw.ElapsedMilliseconds);
// profile new T()
sw.Restart();
for (int index = 0; index < IterationCount; index++)
Test2();
sw.Stop();
Console.WriteLine("Test2 - new T() {0}", sw.ElapsedMilliseconds);
// profile Delegate
sw.Restart();
for (int index = 0; index < IterationCount; index++)
Test3();
sw.Stop();
Console.WriteLine("Test3 - Delegate {0}", sw.ElapsedMilliseconds);
// profile generic new()
sw.Restart();
for (int index = 0; index < IterationCount; index++)
Test4<TestClass>();
sw.Stop();
Console.WriteLine("Test4 - Generic new() {0}", sw.ElapsedMilliseconds);
// generic Activator without bindings
sw.Restart();
for (int index = 0; index < IterationCount; index++)
Test5<TestClass>();
sw.Stop();
Console.WriteLine("Test5 - Generic activator {0}", sw.ElapsedMilliseconds);
// profile Activator with bindings
sw.Restart();
for (int index = 0; index < IterationCount; index++)
Test6<TestClass>();
sw.Stop();
Console.WriteLine("Test6 - Generic activator with bindings {0}", sw.ElapsedMilliseconds);
// profile Baseline
sw.Restart();
for (int index = 0; index < IterationCount; index++)
TestBaseline();
sw.Stop();
Console.WriteLine("Baseline {0}", sw.ElapsedMilliseconds);
}
public static void Test1()
{
var obj = Activator.CreateInstance<TestClass>();
GC.KeepAlive(obj);
}
public static void Test2()
{
var obj = new TestClass();
GC.KeepAlive(obj);
}
static Func<TestClass> Create = delegate
{
return new TestClass();
};
public static void Test3()
{
var obj = Create();
GC.KeepAlive(obj);
}
public static void Test4<T>() where T : new()
{
var obj = new T();
GC.KeepAlive(obj);
}
public static void Test5<T>()
{
var obj = ((T)Activator.CreateInstance(typeof(T)));
GC.KeepAlive(obj);
}
private const BindingFlags anyAccess = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
public static void Test6<T>()
{
var obj = ((T)Activator.CreateInstance(typeof(T), anyAccess, null, null, null));
GC.KeepAlive(obj);
}
static TestClass x = new TestClass();
public static void TestBaseline()
{
GC.KeepAlive(x);
}
}
}
答案 2 :(得分:7)
这取决于您的使用案例。如果您需要非常高的性能并且正在创建许多对象,那么使用Activator.CreateInstance
可能会有问题。
但在大多数情况下它会足够快,而且它是一种非常强大的创建对象的方法。
事实上,大多数IoC容器/服务定位器/您调用它们的任何方法都使用此方法来创建您请求的类型的对象。
如果您担心性能不够好,那么您应该对应用程序进行概要分析,并测量是否存在瓶颈及其位置。我的猜测是,Activator.CreateInstance
的来电不会是您的问题。
答案 3 :(得分:5)
是的,调用
之间存在性能差异(MyClass)Activator.CreateInstance(typeof(MyClass));
和
new MyClass();
后者更快。但是,确定速度下降是否足够大取决于您的域名。在90%的情况下,这不是问题。另请注意,对于值类型,由于涉及unboxing,Activator.CreateInstance
再次变慢。
但是这里是catch :对于泛型类型,它们是相似的。 new T()
在内部拨打Activator.CreateInstance<T>()
,然后拨打RuntimeType.CreateInstanceDefaultCtor(...)。因此,如果您使用通用方法创建T
的新实例,那么它应该无关紧要,尽管具有new()
约束并且调用new T()
更具可读性。这是Jon Skeet关于这个主题的relevant link。
答案 4 :(得分:0)
是的,实际上它存在性能问题(与new()
相比),因为它使用Reflection
,并且静态编译器特别在将参数传递给它(将参数发送给类的构造函数)而不是使用默认值时进行检查构造函数(如下所示)
//Too bad!!!
T someResult = (T)Activator.CreateInstance(
typeof(T),
//parameter
new object[] {id}
);
我认为是否使用它取决于两件事:
首先,您的应用程序类型是可扩展的(当然是典型的流量)
第二个(更重要的是)如何使用Activator.CreateInstance
方法和位置,例如,如果您在每个请求中使用一个或多个构造函数参数(如我提到的使用构造函数参数,速度慢了近十分之一)与不使用无参数(默认构造函数)相比,您的应用程序的性能几乎会大幅下降,但对于另一个实例,如果您使用一次(例如,在application_start上)并且没有构造函数参数< / strong>几乎像new
关键字
这是new()
,Activator.CreateInstance
和Type.GetInstance()
之间的详细基准比较