ActiveX 部件提供的类的实例化

Instancing 属性的值决定该类是私有的(即只在部件内使用),还是可供其它应用程序使用的。

如 Instancing 这个名字所提示的,该属性也决定其它应用程序如何创建该类的实例。该属性的取值代表如下含义:

类模块和工程类型

Instancing 属性的取值在某些工程类型中有限制。各个工程类型中允许的取值见下表:

Instancing 取值 ActiveX EXE ActiveX DLL ActiveX 控件
Private Yes Yes Yes
PublicNotCreatable Yes Yes Yes
MultiUse Yes Yes  
GlobalMultiUse Yes Yes  
SingleUse Yes    
GlobalSingleUse Yes    

从属对象 (PublicNotCreatable)

Instancing 属性的取值决定对象在部件的对象模型中所处的地位,这在“组织对象:对象模型”中详述。

如果一个类的 Instancing 属性值为 PublicNotCreatable,该类的对象就叫做从属对象。从属对象常作为更复杂的对象的一个部分。

例如,一个客户端应用程序要创建多个 Library 对象,但又只想让 Book 对象作为 Library 的一部分。那么可以将 Book 类设置为 PublicNotCreateable,通过在 Library 类中设一个带 Add 方法的 Book 集合让用户给 Library 对象添加新书,这个 Add 方法只能创建属于该集合的新书。

如果有必要,部件可以支持很多个从属对象。可以在集合类的 Add 方法中编写代码来限制该集合中对象的数目,或者只受可用内存的限制。

详细信息   从属对象在本章后面的“从属对象”中详述。

可在外部创建的对象

Instancing 属性的所有取值,除 PublicNotCreatable 和 Private 之外,都可以定义可在外部创建的对象─ 即客户端可以用 New 操作符或 CreateObject 函数创建的对象。

MultiUse 与 SingleUse 的比较

在 ActiveX DLL 工程中,可在外部创建的类的 Instancing 属性一般设为 MultiUse。该设置允许一个进程内部件为客户端可执行程序和其它进程内部件提供该类的任意多个实例。

对于 ActiveX EXE 工程,Instancing 属性的值设为 SingleUse 和 MultiUse 则类的行为大不相同。MultiUse 可以最有效地使用内存,因为它允许部件的一个实例为多个客户端应用程序提供多个对象,而无须复制资源或全局数据。

例如,假设 SmallMechanicals 部件提供 Widget 类,且其 Instancing 属性设为 MultiUse。如果某个客户端应用程序创建了两个 Widget 对象,或者有两个客户端应用程序分别创建了一个 Widget 对象,则所有的 Widgets 都将由该部件的一个实例提供。

如果该 Widget 类的 Instancing 属性设为 SingleUse,则上述两种情景的结果将是当每个 Widget 对象被创建时,都要将该部件的一个独立的副本加载到内存。这种行为的使用及限制在“建立代码部件”一章以及“ActiveX 部件的标准及指南”中论述。

MultiUse 与多线程

如果部件是一个标记为无用户界面执行(即没有用户交互)的 ActiveX EXE,且其 Widget 类的 Instancing 属性设为 MultiUse,则上述两种情景的结果将是两个 Widget 对象在同一个 SmallMechanicals 部件的副本中创建,但有着各自的执行线程。

这里使用了单元模型线程,即每个线程都象一个房间,不同房间内的对象不能意识到其它房间中对象的存在。这是通过给每个 Widget 对象一份自己的 SmallMechanicals 部件的全局数据的副本实现的。

详细信息   使用多线程或将 Instancing 属性值设为 SingleUse 以避免执行拥塞的问题将在“建立代码部件”中论述。

全局对象

提供全局实用函数有很大好处。使部件的用户不必首先创建对象的实例。在进程外部件中,这种功能通常被实现为 Application 对象的属性或方法。

如果类的 Instancing 属性标记为 GlobalMultiUse 或 GlobalSingleUse,不必显式地创建其对象的实例就可以调用该类的属性和方法。

例如,假设 SmallMechanicals 部件要提供一个 GlobalUtility 对象,该对象的方法是通用函数。那么只需添加一个类模块到 SmallMechanicals 工程,将类模块的 Name 属性设为 GlobalUtility,Instancing 属性设为 GlobalMultiUse。

然后给这个类模块添加属性和方法。例如,可以实现一个 ReversePolarity 方法以及一个只读的 WidgetCount 属性:

Public Sub ReversePolarity()
   '(为所有的 Widgets 转换极性的代码)
End Sub

在客户端应用程序中,不必首先创建一个 GlobalUtility 对象就可以调用 ReversePolarity 方法:

Private Sub Command1_Click()
   '不需要对象变量。
   ReversePolarity
End Sub

注意   GlobalMultiUse 对象的属性和方法不是提供对象的部件的全局名称空间的部分。例如,在包含 GlobalUtility 类的工程中,必须显示创建 GlobalUtility 的实例,以使用对象的属性和方法。对全局对象所作的其它限制都在“建立代码部件”的“全局对象与代码库”中列出。

在选择属性和方法的名字时要仔细。使用普通的或常见的名字会导致与其它部件的名字冲突。必须使用类型库的名字来限制属性或方法,从而解决名字冲突的问题:

Private Sub Command1_Click()
   SmallMechanicals.ReversePolarity
   Esalen.ReversePolarity
End Sub

重点   全局对象中的“全局”是指对象所有的属性和方法都位于工程的全局名字空间。这并不意味一个对象可以自动被所有客户端所共享。使用该对象的每一个客户端都获得一个自己的全局对象。

详细信息   本章后面的“为部件提供命名的常数”介绍使用全局对象提供字符串常数和非整数常数的方法。代码部件在“建立代码部件”中深入介绍。