创建运行时确定类型实例的最佳方法

在.NET 4中创建在运行时确定的类型实例的最佳方法是什么?

我有一个实例方法,虽然作用于BaseClass对象可能会被其派生类的实例调用。 我需要在方法中创建与this类型相同的另一个实例。 为每个派生类重载Method是不切实际的,因为它相当复杂,并且更有效地保持单个实现。

 public class BaseClass { //constructors + properties + methods etc public SomeMethod() { //some code DerivedClass d = new DerivedClass(); //ideally determine the DerivedClass type at run-time } } 

我已经阅读了一些关于reflection或使用动态关键字但我没有这些经验。

您正在寻找Activator.CreateInstance (还有其他重载,例如这个接受构造函数参数的重载)。 所以你可以写

 var anotherOneLikeMe = Activator.CreateInstance(this.GetType()); 

这里可能存在一个问题,即anotherOneLikeMe将被输入为object ,因此除非您打算将其BaseClass转换为公共基类(例如,在您的示例中为BaseClass ),否则您无法使用它。

在运行时重复创建实例的性能的最佳方法是编译表达式:

 static readonly Func YCreator = Expression.Lambda>( Expression.New(typeof(Y).GetConstructor(Type.EmptyTypes)) ).Compile(); X x = YCreator(); 

统计(2012年):

  Iterations: 5000000 00:00:00.8481762, Activator.CreateInstance(string, string) 00:00:00.8416930, Activator.CreateInstance(type) 00:00:06.6236752, ConstructorInfo.Invoke 00:00:00.1776255, Compiled expression 00:00:00.0462197, new 

统计(2015年,.net 4.5,x64):

  Iterations: 5000000 00:00:00.2659981, Activator.CreateInstance(string, string) 00:00:00.2603770, Activator.CreateInstance(type) 00:00:00.7478936, ConstructorInfo.Invoke 00:00:00.0700757, Compiled expression 00:00:00.0286710, new 

统计(2015年,.net 4.5,x86):

  Iterations: 5000000 00:00:00.3541501, Activator.CreateInstance(string, string) 00:00:00.3686861, Activator.CreateInstance(type) 00:00:00.9492354, ConstructorInfo.Invoke 00:00:00.0719072, Compiled expression 00:00:00.0229387, new 

完整代码:

 public static X CreateY_New() { return new Y(); } public static X CreateY_CreateInstance() { return (X)Activator.CreateInstance(typeof(Y)); } public static X CreateY_CreateInstance_String() { return (X)Activator.CreateInstance("Program", "Y").Unwrap(); } static readonly System.Reflection.ConstructorInfo YConstructor = typeof(Y).GetConstructor(Type.EmptyTypes); static readonly object[] Empty = new object[] { }; public static X CreateY_Invoke() { return (X)YConstructor.Invoke(Empty); } static readonly Func YCreator = Expression.Lambda>( Expression.New(typeof(Y).GetConstructor(Type.EmptyTypes)) ).Compile(); public static X CreateY_CompiledExpression() { return YCreator(); } static void Main(string[] args) { const int iterations = 5000000; Console.WriteLine("Iterations: {0}", iterations); foreach (var creatorInfo in new [] { new {Name = "Activator.CreateInstance(string, string)", Creator = (Func)CreateY_CreateInstance}, new {Name = "Activator.CreateInstance(type)", Creator = (Func)CreateY_CreateInstance}, new {Name = "ConstructorInfo.Invoke", Creator = (Func)CreateY_Invoke}, new {Name = "Compiled expression", Creator = (Func)CreateY_CompiledExpression}, new {Name = "new", Creator = (Func)CreateY_New}, }) { var creator = creatorInfo.Creator; var sum = 0; for (var i = 0; i < 1000; i++) sum += creator().Z; var stopwatch = new Stopwatch(); stopwatch.Start(); for (var i = 0; i < iterations; ++i) { var x = creator(); sum += xZ; } stopwatch.Stop(); Console.WriteLine("{0}, {1}", stopwatch.Elapsed, creatorInfo.Name); } } public class X { public X() { } public X(int z) { this.Z = z; } public int Z; } public class Y : X { } 

我知道这被标记为reflection,但我通常认为reflection是性能和复杂性的最后手段。 在某些情况下,您的设计/使用需要反思; 但是,我会提供一些备选方案供考虑:

使用工厂Func

 public void SomeMethod(Func createDerived) { BaseClass d = createDerived(); } 

使您的方法使用约束generics类型:

 public void SomeMethod() where TDerived : BaseClass, new() { TDerived d = new TDerived(); } 

在最后一个替代方案中,使用了Activator.CreateInstance正如其他人所建议的那样。 我更喜欢最后一个reflection,因为它们都需要一个无参数构造函数,但编译器强制执行约束,派生类型必须具有无参数构造函数,而reflection方法会导致运行时exception。

这里的问题是你在编译时永远不会知道DerivedClass的类型。

但是你可以做这种事情:

 BaseClass obj = new DerivedClass(); 

这是这样实现的:

 BaseClass obj = (BaseClass)Activator.CreateInstance(this.GetType()); 

如果DerivedClass没有无参数构造函数,则此调用将失败。

这实际上取决于“运行时”的含义以及目标是什么。 例如,Jon和Bas都想到了使用Reflection来延迟绑定特定类并在运行时实例化它的想法。 这当然是一个想法,如果这是您的目标,您甚至可以在运行时发现对象上的方法

如果您使用的是接口,则还有其他几个选项。 Microsoft可扩展性框架(或MEF)可能是您想要查看的选项,因为它包括运行时的可发现性和实例化。 缺点是发现的类必须遵循正确的接口,但在大多数情况下这不是一个真正的问题。

如果您知道要加载的类,并且它们具有相同的接口(公共主题),但想要在运行时实例化不同的类,则可以选择IoC容器。 这不是你要问的特别之处。

动态关键字不是您想要的。 它确实在运行时加载,但dyanmic更多的是编译器没有检查你调用的方法是否确实存在。 如果做得不正确,当你调用一个不存在的方法时,你最终会得到一个有趣的爆发。 我见过的关于动态的主要动机是与动态语言的交互,比如IronPython。

 public void SomeMethod() { object x =Activator.CreateInstance(this.GetType()); } 

这应该创建一个新实例,另一方面我想知道你为什么要这样做。

虽然您确实需要使用Activator.CreateInstance,但您可能需要特别注意Activator.CreateInstance(String,String) ,它可以使用您在运行时可能知道的类的名称来调用。

如果要在基本类型上实例化派生类型,这将非常有用。 如果您要从派生类型本身调用SomeMethod ,则使用this.GetType()的先前答案应该足够了。