在 Visual Basic 中,可以为通用的过程创建过程库,方法是把通用过程实现为类模块。这种类模块的 Instancing 属性应设置为除 Private 和 PublicNotCreatable 以外的任何值,以便客户端可以创建该类的实例。
如果类的 Instancing 属性的值设为 GlobalMultiUse,那么在该工程完成了之后,在使用类的属性和方法时不必显式地创建该类的实例。
GlobalMultiUse 对象(或全局对象)的属性和方法被添加到使用该对象的任何工程的全程命名空间中。也就是说,可以在别的工程中引用该部件,而且该全局对象的属性和方法的名称是可全程识别的,如同 Visual Basic 的一部分。
重点 当从其它工程引用该部件时,全局对象的属性和方法将成为全局命名空间的一部分。在创建这个 GlobalMultiUse 类模块的工程内,该类创建的对象全部为普通对象。
下面的代码段演示了 MultiUse 和 GlobalMultiUse 对象的区别。二者都假定在“引用”对话框中设置了对提供 Financials 对象的部件的引用,该 Financials 对象的方法提供了通用的财务函数。
使用 MultiUse Financials 对象时需要的代码:
'
这些代码在标准模块中执行。'
定义一个包含实例的全局变量。Public gfins As New Financials
'
这些代码在使用Financials
对象的方法'
的窗体中执行。Private Sub cmdCalculateResult_Click()
txtResult.Text = gfins.LeastReasonableReturn( _
CCur(txtBeginningBalance.Text))
End Sub
注意 如果使用上述代码,在代码中第一次使用变量 gfins
时需要创建 Financals 对象。这样,在每次函数调用时要增加一点额外的开销。要避免这个问题,可以在 Sub Main 中(对独立方式的可执行程序而言)或者在创建第一个对象时(对部件而言)显式地创建该 Financials 对象。
作为对比,使用 GlobalMultiUse Financials 对象的代码就不需要全局变量:
Private Sub cmdCalculateResult_Click()
txtResult.Text = LeastReasonableReturn( _
CCur(txtBeginningBalance.Text))
End Sub
当第一次执行到包含 Financials 类的方法的代码行时,Visual Basic 将创建一个该类的隐性实例。
注意 本主题的稍后部分将讲到,如果使用隐性的实例,和前面的代码示例中定义为 As New 的 gfins
一样,每次调用时也会增加一点额外开销。
在“对象浏览器”中,作为工程的全程命名空间的一部分的过程名显示在“类”列表的 <globals> 项中。图 8.1 以使用 Financials 类的工程组为例加以说明。
图 8.1 GlobalMultiUse 类的方法显示在 <globals> 项中
“对象浏览器”用粗体字显示出工程中定义的类名和成员名。
提示 如果要查看定义这些类和成员的代码,则可直接双击粗体字显示的名称。
将一个类的 Instancing 属性设置为 GlobalMultiUse 或 GlobalSingleUse 会使客户程序把这个类的属性和方法当作全局函数来使用,但是在定义这个 GlobalMultiUse 类模块的工程中,从这个类创建的对象并不是全局的。
例如,在 MyUtilities 工程中包含了一个 Utilities 类,它的 Instancing 属性被设成 GlobalMultiUse,同时这个类有一个名为 InvertMatrix 的方法。这样,任何引用 MyUtilities 的客户工程都可以象调用全局函数那样调用 InvertMatix。但是,在 MyUtilities 工程内部不能把 InvertMatrix 作为全局函数使用。
如果希望在 MyUtilities 工程内部使用 Utilities 类的全局实例,可以在 MyUtilities 的标准模块中增加如下声明:
Public Utilities As New Utilities
这样,在需要使用 InvertMatrix (或者由 Utilities 类提供的其它过程)时,可以象下面这样用类名来限制它:
Utilities.InvertMatrix aintMyLargeMatrix
在第一次以这种方式使用 Utilities 类的某个方法时,将会自动创建这个类的一个实例,因为全局变量是被声明为 As New。使用类名作为变量的名称可以明了地说明是部件中的哪个模块提供了这个过程。
注意 要运用这个技术就必须在标准模块而不是类模块中声明全局变量。
Visual Basic < globals > 的属性可以是对象,例如 App 对象和 Printers 集合。可以为自己编写的全局类创建类似的对象属性。
假设已经编写了一个用于访问 Windows 注册表的 Registry 类,而且拥有一个 Globals 类的 Instancing 属性为 GlobalMultiUse。下面的代码段为 Globals 类添加了一个 Registry 属性:
Private mRegistry As Registry
Private Sub Class_Initialize()
Set mRegistry = New Registry
End Sub
Public Property Get Registry() As Registry
Set Registry = mRegistry
End Sub
在编译了该部件之后即可在其它工程中引用。如果 Registry 类中有一个 FindKeyContaining 方法,则可以编写如下代码来使用该方法:
Private Sub Command1_Click()
lblResult.Caption = _
Registry.FindKeyContaining(txtInput.Text)
End Sub
上述代码假定窗体中包含了一个文本框,用来接收用户输入的字符串 (txtInput
);以及一个标签用来显示注册表项 (lblResult
)。请注意,在使用 Registry 对象之前不需要创建 Globals 的实例。
提示 如果给全局对象添加了对象属性,那么经常使用 Property Get。要是使用公共变量的话,如果不慎将提供对象属性设置为 Nothing 就会破坏该对象。
要用好全局对象,请注意下述事项:
例如,假设同时使用了 MyUtilities 和 YourUtilities,YourUtilities 有一个 GlobalMultiUse 类的名称为 General,而且 General 类也有一个名称为 LeastReasonableReturn 的方法。要确保使用 YourUtilities 的方法,可以编写如下代码:
Private Sub cmdCalculateResult_Click()
txtResult.Text = _
YourUtilities.LeastReasonableReturn( _
CCur(txtBeginningBalance.Text))
End Sub
重点 如果没有对可能发生冲突的名称加以限定,Visual Basic 就使用“引用”对话框中显示在最前面的库。除非两个过程有不同的参数,否则编译时不会报错。
重点 全程命名空间的污染是个很严重的问题。具体的示例与论述,请参阅《程序员指南》中的“用部件编程”中的“创建对对象的引用”。
重点 使用 GlobalMultiUse 类的属性和方法的每个客户端都将获得属于自己的一个类实例。换句话说,GlobalMultiUse 中的 “Global” 一词并不意味着“所有客户端共享同一个全局实例”。
如果要确定是使用全局对象,还是显式地定义变量来存放对象的实例,可以进行下列判断:
只有进程外部件才可以将类模块的 Instancing 属性设为 GlobalSingleUse。如果这样做,每个客户端的内存都将装入该部件的一个独立实例。这比使用 GlobalMultiUse 对象将需要更多的内存。
如果想让每个客户端都用一个执行线程来执行全局过程,那么可以考虑使用 GlobalMultiUse 全局对象,并使进程外部件成为多线程的。请参阅“Scalability 和 Multithreading”。
注意 在 ActiveX DLL 工程中不允许使用 GlobalSingleUse,因为一个客户端的进程空间不能装入同一 DLL 的多个实例。
详细信息 请参阅“多过程的可扩展性:SingleUse 对象”。