65.9K
CodeProject 正在变化。 阅读更多。
Home

实例化对象的替代方法

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.76/5 (12投票s)

2012年5月2日

CPOL

4分钟阅读

viewsIcon

40875

downloadIcon

296

面向初学者的动态实例化外部类型指南。

介绍 

在大多数情况下,我们在编写应用程序时,都使用关键字 "new" 来实例化对象,如下所示:
Dim obj As New AnyObject
在本文中,我们将看到另外两种在不使用关键字 "new" 的情况下动态实例化对象的方法。 

背景  

在我最近参与的一个项目中,其中一个要求是根据应用程序外部的 System.Types 动态实例化对象。

这是一个 UserControl,其目的是在 PropertyGrid 中显示实例化对象的属性,以便只有用户在 PropertyGrid 中修改的属性才会被持久化,并在每次创建新实例时重新应用到对象。

要加载的 System.Type 是由用户通过告知磁盘上的 assembly 位置以及要实例化的 System.Type 来提供的。

在测试阶段,我使用了我在其他项目中创建的其他程序集,以及其他人创建的程序集,以确保它无论类如何都能正常工作。

在此期间,我发现了一些通过反射实例化不起作用的情况。

通过反射实例化

 这种类型的实例化被大多数需要动态创建应用程序外部 System.Type 实例的程序员广泛使用。

通过使用 Assembly.CreateInstance(string) 实例化类型时,所有实例化过程都正常进行,就像您使用关键字 "new" 一样,也就是说,类的构造函数被执行,并且其所有字段都正确初始化。

Dim obj1 As Object = _assembly.CreateInstance("MyExternalAssembly.AnyObject") 

在上面的示例中,我们正在程序集 "MyExternalAssembly" 中实例化一个名为 "AnyObject" 的类。 

在这种情况下,您必须提供一个包含 System.Type 完整名称的字符串,包括程序集名称和命名空间(如果有),以及类的名称,并且它必须存在于 _assembly 对象中,否则实例化不会发生。

注意:在内部,在检查提供的 System.Type 字符串是否存在于加载的 assembly 中之后,它会运行 Activator.CreateInstance(System.Type) 方法,该方法将实例返回给调用者。

通过序列化/反序列化实例化

我决定将这种实例化方式称为 "序列化/反序列化",因为它在 .Net Framework 内部用于反序列化 WCF DataContracts

在我参与的项目的测试阶段,有几次我遇到了类构造函数中的逻辑阻止对象实例化的情况,特别是当它是一个商业组件时,其许可机制位于类的构造函数中,并且在违反许可规则时会引发异常。

注意:在我看来,一个好的许可和验证机制不仅必须依赖类的构造函数,还必须依赖类的关键方法以及其内部的其他类。

在这种情况下,通过反射实例化类是不可能的,因为通过反射以及通过关键字 "new",构造函数在实例化期间总是运行,从而阻止其完成。

通过 Reflector 分析 .Net framework 代码,我发现 WCF 的 DataContracts 在反序列化对象时从不调用任何类的构造函数,并且它们的字段也没有用可能已定义的 "默认" 值初始化。

字段未初始化的原因是有道理的。据我理解,这是一种获得性能的方式,因为在反序列化期间 - 在对象创建之后 - 这些字段将接收到与 "默认" 值不同的值,这样它们被赋值两次(一次是 "默认" 值,另一次是序列化的值)就没有意义了。

我仍然不明白为什么不调用构造函数,也许也是出于性能原因?我不知道...

下面是不运行构造函数或初始化字段的实例化代码: 

Dim type = _assembly.GetType("MyExternalAssembly.OtherObject")
Dim obj1 As Object = System.Runtime.Serialization.FormatterServices.GetUninitializedObject(type) 


请注意,GetUninitializedObject() 方法接受一个 System.Type,它可以是程序集本身中现有类的 System.Type,也可以是外部程序集中的 System.Type,但是外部程序集应该事先加载。

在上面的示例中,我们使用了应用程序外部的程序集中的 System.Type,其构造函数抛出 InvalidOperationException,因此如果我们使用标准实例化(使用关键字 "new")或通过反射实例化,我们将永远无法实例化该类的对象。

使用源代码

本文源代码的解决方案中有两个项目,一个 Class Library 和一个 Windows Form Application 

"MyExternalAssembly" 是一个 Class Library,它有两个类,一个是名为 "AnyObject" 的类,允许使用本文中提到的任何实例化方式;另一个类名为 "otherObject",它是一个默认构造函数会抛出异常的类,因此它只能通过本文中介绍的 "序列化/反序列化" 过程进行实例化。

项目 PropertyInspector 是一个 Windows Forms Application,它允许您选择一个外部程序集(在本例中是 MyExternalAssembly 程序集),并将其字段显示在 PropertyGrid 中,如下所示:

 

 

总结 

本文的目的是向初学者展示实例化对象的替代方法,而不是选择最佳的实例化对象方法或 "更正确" 的方法。

也许还有其他在 .NET 中实例化对象的方法我可能还不知道,如果您知道,请随意使用 "评论和讨论" 部分与我们分享。 

根据用法,一种方法在性能、可用性等方面可能比另一种方法更有优势。 

© . All rights reserved.