重拾VB616:Object Model, Collection Class, and ActiveX Designer

来自MSDN-2001-OCT: Visual Tools and Languages/Visual Studio 6.0 Documentation/Visual Basic Documentation/Using Visual Basic/Programmer’s Guide/Part 2: What Can You Do With Visual Basic/Programming with Objects/Object Models

1. How to keep track of all object references

(1) According to COM rules,the only information you can depend on is whether or not the reference count is zero. You know when the reference count reaches zero,because your object's Terminate event occurs.

To make using objects more efficient,the Component Object Model (COM) specifies a number of complex shortcuts to its reference counting rules. The net result is that you couldn't trust the value of the reference count even if you had access to it.

(2) Declare your object variables as class types,instead of As Object.

(3) For collections of object references,don't use the Visual Basic Collection object by itself.

(4) Organize your object into a hierarchy,so that it's easy to walk through the whole model and reports on all the existing objects.

(5) Don't declare variables As New,because if you use one after you've set it to Nothing,Visual Basic obligingly creates another object.

(6) Circular references are the most difficult kind to shut down cleanly.

2. Object Models

(1) 可以用成员对象的办法来express对象之间的hierarchy。比如:

' Code for the Declarations section of the
' SmallBusiness class module.
Public Name As String
Public Product As New Product
Public Employees As New Collection
Public Customers As New Collection

Object properties work well when the relationship between objects is one-to-one. It frequently happens,however,that an object of one type contains a number of objects of another type. 这时候我们就要用collection。

(2) Class Builder creates robust object properties and collection classes,and allows you to rearrange your model easily.

(3) Tip When you assign a Parent property to an object in a collection,don't use a reference to the Collection object. The real parent of the object is the object that contains the collection. If the Parent property points to the collection,you'll have to use two levels of indirection to get to the real parent — that is,obj.Parent.Parent instead of obj.Parent.

(4) One of the biggest problems with Parent properties is that they create circular references. 解决的办法是给父亲object设计一个teardown方法,先把它的所有孩子都set nothing,然后再set它自己nothing.

When a Collection object is destroyed,Visual Basic sets all the object references it was holding to Nothing.

If there are variables anywhere in your program that still contain references to the SmallBusiness object,or to any of the objects it contains,those objects won't be destroyed. Part of the cleanup for your program must be to ensure that all object variables everywhere are set to Nothing.

To test whether this is happening,you may want to add some debugging code to your objects. For example,you can add the following code to a standard module:

VB.net语言: Dim frm As New Form1
' Global debug collection
Public gcolDebug As New Collection

' Global function to give each object a unique ID.
Public Function DebugSerial() As Long
Static lngSerial As Long
lngSerial = lngSerial + 1
DebugSerial = lngSerial
End Function

In each class module,you can put code similar to the following. Each class provides its own name where "Product" appears.

' Storage for the debug ID.
Private mlngDebugID As Long

Property Get DebugID() As Long
DebugID = mlngDebugID
End Property

Private Sub Class_Initialize()
mlngDebugID = DebugSerial
' Add a string entry to the global collection.
gcolDebug . Add "Product Initialize; DebugID=" _
& DebugID , CStr( DebugID)
End Sub

Private Sub Class_Terminate()
' Remove the string entry,so you know the object
' isn't around any more.
gcolDebug . Remove CStr( DebugID)
End Sub

At any time,you iterate over the global collection to see what objects haven't been destroyed.

3. Creating Your Own Collection Classes

(1) The ID property is the key for retrieving or deleting an Employee object from the collection,so it must be set once and never changed. This is accomplished with a Static Boolean variable that is set to True the first time the property is set. The property can always be read,because there is a Property Get.

Option Explicit
' Properties of the Employee class.
Public Name As String
Public Salary As Long

' Private data for the write-once ID property.
Private mstrID As String

Property Get ID() As String
ID = mstrID
End Property

' The first time the ID property is set,the static
' Boolean is also set. Subsequent calls do nothing.
' (It would be better to raise an error,instead.)
Property Let ID( strNew As String)
Static blnAlreadySet As Boolean
If Not blnAlreadySet Then
blnAlreadySet = True
mstrID = strNew
End If
End Property

(2) There are three general approaches you can take to implementing object containment using collections.

a) Public Collection: In the SmallBusiness class module,declare an Employees variable As Collection,and make it Public. This is the cheap solution.

The Collection object's very flexibility betrays it — you can put anything into a Collection,including the KitchenSink object.

b) Private Collection: In the SmallBusiness class module,declare an mcolEmployees variable As Collection,and make it Private. Give the SmallBusiness object a set of methods for adding and deleting objects. This is the least object-oriented of the three designs.

You can gain some robustness by making the Collection object private,but you lose the ability to use For Each … Next with the collection.

c) Implement your own collection class,by creating a collection class module named Employees,as described later in this chapter. Give the SmallBusiness object a read-only property of the Employees class.

Creating your own collection class: gives you the robustness of encapsulation,and as a bonus you get back the ability to use For Each … Next.

(3) Important In order for your collection classes to work with For Each … Next,you must provide a hidden NewEnum method with the correct procedure ID.

' NewEnum must return the IUnknown interface of a
' collection's enumerator.
Public Function NewEnum() As IUnknown
Set NewEnum = mcolEmployees . [ _NewEnum ]
End Function

The important thing you're delegating to the Collection object is its enumerator. An enumerator is a small object that knows how to iterate through the items in a collection.

The square brackets around the Collection object's _NewEnum method are necessary because of the leading underscore in the method name. This leading underscore is a convention indicating that the method is hidden in the type library.

You can't name your method _NewEnum,but you can hide it in the type library and give it the procedure ID(-4) that For Each … Next requires.

(4) Steps to Implement a Collection Class:

a) Add a class module to your project,and give it a name — usually the plural of the name of the object the collection class will contain.

b) Add a private variable to contain a reference to the Collection object your properties and methods will delegate to.

c) In the Class_Initialize event procedure,create the Collection object.

d) Add a Count property and Add,Item,and Remove methods to your class module; in each case,delegate to the private Collection by calling its corresponding member.

e) When you implement the Add method,you can override the behavior of the Collection object's undiscriminating Add method by accepting only objects of one type.

f) Use the Procedure Attributes dialog box to make the Item method the default for your collection class.

g) Add a NewEnum method,as shown below. Use the Procedure Attributes dialog box to mark it as hidden,and to give it a Procedure ID of –4 so that it will work with For Each … Next.

Public Function NewEnum() As IUnknown
Set NewEnum = mcol . [ _NewEnum ]
End Function

Note The code above assumes that the private variable in step 2 is named mcol.

h) Add custom properties,methods,and events to the collection class.

Note The Class Builder utility,included in the Professional and Enterprise editions of Visual Basic,will create collection classes for you. You can customize the resulting source code.

4. ActiveX Designers

(1) A designer provides a visual design window in the Visual Basic development environment. You can use this window to design new classes visually.

Objects created from the classes you design in this fashion have separate design-time and run-time behavior and appearance,although many objects — such as forms and controls — look very similar in the two modes.

(2) Difference between ActiveX Designer Classes to ActiveX controls:

  • If an object created from an ActiveX designer class is visible at run time,it has its own window. It is not contained within another form,as ActiveX controls are.
  • Like form classes,but unlike ActiveX controls,the classes produced by ActiveX designers are private classes. You cannot declare public methods that use these classes as argument types or return types.
  • Public Function A() As UseConnection1 'Error
    Public Sub B( CallBack As UseConnection1) 'Error

    (3) Creating ActiveX Designers: You can use the ActiveX Designer Software Development Kit to create new ActiveX designers for use with Visual Basic.

    Note The ActiveX Designer SDK requires a C++ compiler,such as Microsoft Visual C++. ActiveX designers cannot be written using Visual Basic.

    (4) Adding an ActiveX Designer to the Project Menu: Project->Components->Designers

    (5) Like all designers,the Microsoft Forms designer has its own run-time .dll. Using this designer in a Visual Basic project will therefore increase the memory requirements of the resulting executable.

    (6) Inserting a New Instance of an ActiveX Designer: On the Project menu,click Add designer (where designer is the name of the designer) to display a list of installed designers. Pick the designer you want from the list.

    (7) ActiveX Designers and SourceCode Control: When attempting to edit a file created by a designer,you may not be prompted to check out the file before editing. Additionally,some designers write files out to the file system; SourceCode control may not be aware of these files.

    相关文章

    Format[$] ( expr [ , fmt ] ) format 返回变体型 format$ 强...
    VB6或者ASP 格式化时间为 MM/dd/yyyy 格式,竟然没有好的办...
    在项目中添加如下代码:新建窗口来显示异常信息。 Namespace...
    转了这一篇文章,原来一直想用C#做k3的插件开发,vb没有C#用...
    Sub 分列() ‘以空格为分隔符,连续空格只算1个。对所选...
      窗体代码 1 Private Sub Text1_OLEDragDrop(Data As Dat...