组件以对象的形式提供可重用的代码。通过创建对象并调用它们的属性和方法来使用组件代码的应用程序称为“客户端”。客户端与它使用的组件可能位于同一个程序集中,也可能位于不同的程序集中。
以下各个过程相互关联,因此过程的执行顺序非常重要。
创建项目
创建 CDemoLib 类库和 CDemo 组件
- 从“文件”菜单中选择“新建”,然后选择“项目”以打开“新建项目”对话框。从“Visual Basic 项目”列表中选择“类库”项目模板,然后在“名称”框中输入“CDemoLib”。
注意 “类库”模板在 Visual Basic .NET 标准版中不可用。有关更多信息,请参见 Visual Basic 标准版功能。
提示 每次创建新项目时都要为其指定名称。这样就设置了根命名空间、程序集名称和项目名称,同时确保了默认组件位于正确的命名空间中。
- 在解决方案资源管理器中,右击“CDemoLib”,然后从快捷菜单中选择“属性”。注意,“根命名空间”框中包含“CDemoLib”。
根命名空间用于限定程序集中的组件名。例如,如果两个程序集都提供了名为 CDemo 的组件,则可以使用
CDemoLib.CDemo
指定您的 CDemo 组件。单击“取消”关闭对话框。
- 从“项目”菜单中选择“添加组件”。在“添加新项”对话框中,选择“组件类”并在“名称”框中键入 CDemo.vb。名为 CDemo.vb 的组件即添加到您的类库中。
- 在解决方案资源管理器中,右击 CDemo.vb 并选择“查看代码”。代码编辑器打开。
注意紧跟在
Public Class CDemo
下面的Inherits System.ComponentModel.Component
。本节指定您的类从中继承的类。默认情况下,组件从系统提供的 Component 类继承。Component 类为组件提供了许多功能,包括使用设计器的能力。 - 在解决方案资源管理器中,右击 Class1.vb 并选择“删除”。这将删除与类库一起提供的默认类,因为本演练中将不使用该类。
- 从“文件”菜单中,选择“全部保存”来保存项目。
添加构造函数和终结程序
构造函数控制组件的初始化方式;Finalize 方法控制组件的拆卸方式。CDemo 类的构造函数和 Finalize 方法中的代码维持对现有 CDemo 对象数的实时计数。
添加 CDemo 类的构造函数和终结程序的代码
- 在代码编辑器中,添加成员变量以保留 CDemo 类实例的运行总计,并为每个实例添加 ID 号。
Public ReadOnly InstanceID As Integer Private Shared NextInstanceID As Integer = 0 Private Shared ClassInstanceCount As Long = 0
由于
InstanceCount
和NextInstanceID
成员变量已声明为 Shared,因此它们仅存在于类级别上。所有访问这些成员的 CDemo 实例都将使用相同的内存位置。当在代码中第一次引用 CDemo 类时,共享成员将被初始化。这可能是首次创建 CDemo 对象或者是首次访问该类的共享成员之一。 - 找到 Public Sub New() 和 Public Sub New(Container As System.ComponentModel.IContainer),即 CDemo 类的默认构造函数。在 Visual Basic .NET 中,所有构造函数均被命名为 New。组件可以有若干带有不同参数的构造函数,但是它们必须都具有名称 New。
注意 类的构造函数访问级别决定哪些客户端可以创建该类的实例。在 Visual Basic 的其他版本中,对象创建由“实例化”(Instancing) 属性控制;如果已经使用了 Instancing 属性,您可能会发现阅读 Visual Basic 中组件实例化的更改是有用的。
- 将下面的代码添加到 Sub New(),以在创建新的 CDemo 时递增实例计数和设置实例 ID 号。您可能需要在代码编辑器中展开“组件设计器生成的代码”框。
注意 始终在对 InitializeComponent 的调用之后添加代码。此时,任何构成组件都已初始化。
InstanceID = NextInstanceID NextInstanceID += 1 ClassInstanceCount += 1
作为 ReadOnly 成员,
InstanceID
只能在构造函数中设置。注意 熟悉多线程处理的用户能正确地认识到分配
InstanceID
和递增NextInstanceID
应属于原子操作。 演练:用 Visual Basic 创作简单的多线程组件中对此问题和其他与线程处理相关的问题进行了阐释。 - 在构造函数的结尾后面添加以下方法:
Protected Overrides Sub Finalize() ClassInstanceCount -= 1 End Sub
内存管理器在最后回收被 CDemo 对象占用的内存前调用 Finalize。Finalize 方法自 Object(.NET 类层次结构中所有引用类型的根)产生。通过重写 Finalize,可以在组件从内存中被移除前执行清除操作。但是,正如在本演练的随后部分所看到的,完全有理由更早释放资源。
将属性添加到类中
CDemo 类只有一个属性,它是一个共享属性,允许客户端获悉任何给定时刻内存中的 CDemo 对象数。可以用类似的方法创建方法。
创建 CDemo 类的属性
- 将以下属性声明添加到 CDemo 类中,以允许客户端检索 CDemo 的实例数。
Public Shared ReadOnly Property InstanceCount() As Long Get Return ClassInstanceCount End Get End Property
注意 属性声明语法不同于 Visual Basic 早期版本中使用的属性声明语法。有关语法更改的更多信息,请参见 。
测试组件
若要测试组件,则需要一个使用该组件的项目。此项目必须是按下“运行”按钮后启动的第一个项目。
添加 CDemoTest 客户端项目作为解决方案的启动项目
- 在“文件”菜单中,指向“添加项目”,然后选择“新建项目”以打开“添加新项目”对话框。
- 从“Visual Basic 项目”列表中选择“Windows 应用程序”项目模板,在“名称”框中键入“CDemoTest”,然后单击“确定”按钮。
- 在解决方案资源管理器中,右击“CDemoTest”,然后单击快捷菜单中的“设为启动项目”。
为了使用 CDemo 组件,客户端测试项目必须具有对类库项目的引用。添加引用后,最好将 Imports 语句添加到测试应用程序中以简化组件的使用。
添加对类库项目的引用
- 在解决方案资源管理器中,右击紧跟在 CDemoTest 下方的“引用”节点,然后从快捷菜单中选择“添加引用”。
- 在“添加引用”对话框中,选择“项目”选项卡。
- 双击“CDemoLib”类库项目,将其添加到“选定的组件”列表中。“CDemoLib”将出现在 CDemoTest 项目的“引用”节点下面。单击“确定”以接受选择。
- 在解决方案资源管理器中,右击“Form1.vb”,然后从快捷菜单中选择“查看代码”。
通过添加对 CDemoLib 的引用,您可以使用 CDemo 组件的完全限定名,即 CDemoLib.CDemo。
添加 Imports 语句
- 将下面的 Imports 语句添加到 Form1 的代码编辑器顶部类声明的上面:
Imports CDemoLib
添加 Imports 语句使您得以省略库名,并将组件类型称为 CDemo。有关 Imports 语句的更多信息,请参见。
现在您将创建并使用测试程序来测试您的组件。
理解对象生存期
CDemoTest 程序将通过创建和释放大量的 CDemo 对象来阐释 .NET Framework 中的对象生存期。
添加创建和释放 CDemo 对象的代码
- 单击“Form1.vb [设计]”返回设计器。
- 将“按钮”和“计时器”从工具箱的“Windows 窗体”选项卡中拖到 Form1 设计图面上。
非可视化的 Timer 组件出现在窗体下方一个单独的设计图面上。
- 双击 Timer1 的图标以创建 Timer1 组件的 Tick 事件的事件处理方法。将以下代码放置在事件处理方法中。
Me.Text = "CDemo instances: " & CDemo.InstanceCount
每当计时器走过一个刻度,窗体标题都显示 CDemo 类的当前实例计数。类名用作共享 InstanceCount 属性的限定符(不必通过创建 CDemo 的实例来访问共享成员)。
- 找到 Form1 (Sub New) 的构造函数,并在调用 InitializeComponent 后添加下面的代码。
Timer1.Enabled = True
这样就会在创建窗体之后立即启动计时器。
- 单击“Form1.vb [设计]”选项卡返回设计器。
- 双击 Form1 上的“按钮”,以创建按钮的 Click 事件的事件处理方法。将以下代码放置在事件处理方法中。
Dim cd As CDemo Dim ct As Integer For ct = 1 To 1000 cd = New CDemo Next
此代码看起来可能很陌生。每创建一个 CDemo 实例时,前一个实例即被释放。当 For 循环完成时,将只留下一个 CDemo 实例。当事件处理方法退出时,即便是该实例也将被释放,因为变量
cd
会超出范围。正如您可能已经猜到的,事实并非完全如此。
运行并调试 CDemoTest 和 CDemo 项目
- 按 F5 键启动解决方案。
客户端项目将启动,并显示 Form1。注意,此窗体的标题显示为“CDemo 实例:0”。
- 单击按钮。窗体标题应显示为“CDemo 实例:1000”。
等到按钮的 Click 事件处理过程完成时,CDemo 的实例已全部释放。为什么它们没有被回收呢?简言之,内存管理器在后台以较低的优先级终结对象。只有当系统的可用内存较少时,此优先级才会提高。这种“懒惰”的垃圾回收方案允许快速分配对象。
- 继续单击该按钮若干次,同时监视标题的变化。某些时候,实例数会骤然减少。这意味着内存管理器已经回收了某些对象的内存。
注意 如果已单击了 10 次以上,而 CDemo 实例的数目尚未减少,则可能需要调整代码以使它能够使用更多的内存。关闭窗体返回开发环境,并将 For 循环中的迭代数目增至 10000。然后再次运行项目。
- 重复步骤 3。这一次您将在内存管理器回收更多的对象之前取得更大的进展。
实际上,每当您重复步骤 3 时,您都可能能够在内存管理器介入之前分配更多的 CDemo 对象。这是因为越来越多的 Visual Studio 部分被交换出来,从而为 CDemo 实例留出了更多的空间。
- 关闭窗体返回开发环境。