2012-03-20 68 views
11

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

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

public class BaseClass 
{ 
    //constructors + properties + methods etc 

    public SomeMethod() 
    { 
      //some code 

      DerivedClass d = new DerivedClass(); //ideally determine the DerivedClass type at run-time 
    } 
} 

我读过一些关于反射或使用动态关键字,但我没有这些经验。

+0

最佳可能是[表达式树IMO(http://stackoverflow.com/questions/6582259/fast-creation-of-objects-instead-of-activator- createinstancetype) – nawfal 2013-06-12 21:04:31

回答

9

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

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

有可能在这里是anotherOneLikeMe一个问题是要分型为object,所以,除非你打算把它转换为一个共同的基类(例如,在你的例子BaseClass)没有太多你可以用它做。

2

这里的问题是您在编译时无法知道DerivedClass的类型。

但是,您可以做这种类型的事情:

BaseClass obj = new DerivedClass(); 

这是这样实现的:

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

如果DerivedClass无参数构造函数,虽然该调用失败。

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

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

1

这真的取决于你的意思是“运行时间”以及目标是什么。例如,Jon和Bas都使用了Reflection来延迟绑定某个特定的类,并在运行时对其进行了实例化。这当然是一个想法,你甚至可以在运行时发现对象的方法,如果这是你的目标

如果你使用接口,你有两个附加选项。 Microsoft扩展性框架(或MEF)可能是您想要查看的选项,因为它包含运行时的发现性和实例化。缺点是发现的类必须坚持正确的界面,但在大多数情况下这不是真正的问题。

如果您知道正在加载的类,并且它们具有相同的接口(常见主题),但希望在运行时实例化不同的类,则IoC容器是一个选项。这并不是你所问的。

动态关键字不是你正在寻找的。它在运行时会加载,但是dyanmic更多的是关于编译器不检查你调用的方法是否真的存在。如果做得不正确,当您调用一个不存在的方法时,最终会出现一个有趣的爆炸。我所见过的迪亚尼亚姆的主要动机是与动态语言(如IronPython)的互动。

4

我知道这被标记为反射,但我通常将反射看作是性能和复杂性原因的最后手段。在某些情况下,您的设计/使用需要反思;然而,我将提供一些值得考虑的备选方案:

使用工厂Func

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

让你的方法使用受限的泛型类型:

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

在幕后这最后的选择品牌像其他人所建议的那样使用Activator.CreateInstance。我更喜欢最后一次反射,因为它们都需要无参数构造函数,但编译器强制约束派生类型必须具有无参数构造函数,而反射方法会导致运行时异常。

+0

还要考虑工厂'Func'方法对于可以调用派生类的参数化构造函数的Lambda表达式打开。例如:'SomeMethod(()=> new MyDerived(DateTime.Now);' – devgeezer 2012-03-20 14:52:04

0

虽然确实需要使用Activator.CreateInstance但您可能想要专门查看Activator.CreateInstance(String, String),可以使用您在运行时可能知道的类的名称来调用它。

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

17
性能反复创建实例在运行时

最好的办法是编译表达式:

static readonly Func<X> YCreator = Expression.Lambda<Func<X>>(
    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,64):

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<X> YCreator = Expression.Lambda<Func<X>>(
    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<X>)CreateY_CreateInstance}, 
     new {Name = "Activator.CreateInstance(type)", Creator = (Func<X>)CreateY_CreateInstance}, 
     new {Name = "ConstructorInfo.Invoke", Creator = (Func<X>)CreateY_Invoke}, 
     new {Name = "Compiled expression", Creator = (Func<X>)CreateY_CompiledExpression}, 
     new {Name = "new", Creator = (Func<X>)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 += x.Z; 
    } 
    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 { } 
+0

很高兴看到构造函数调用比激活器更快,就像这里确认的那样http://blogs.msdn.com/b/haibo_luo /archive/2005/11/17/494009.aspx和这里http://bloggingabout.net/blogs/vagif/archive/2010/04/02/don-t-use-activator-createinstance-or-constructorinfo-invoke-使用编译lambda表达式.aspx – nawfal 2013-05-02 07:30:52

+1

6.6236752调用选项中的第一个6是一个错字? – Jeff 2015-04-16 01:34:00

+0

@Jeff,我也认为“这不可能是正确的!”。但它似乎是:我粘贴的代码进入Linqpad,启用优化,结果或多或少一致(已经过了3年),按照与DarkGray相同的顺序:00:00:00.4287503,00:00:00.4431353,** 00:00:01.0507991 **,00:00:00.0809354和00:00:00.0231360。 – easuter 2015-04-30 13:19:03

相关问题