Saturday, December 26, 2009

Variant properties

Properties that return Variant values aren’t different from other properties: You just need to declare a Public Variant member and you’re done. But things are a bit more complex if the property can receive either a regular value or an object value. For example, say that you want to implement a CurrentAddress property, but you want to keep it more flexible and capable of storing either a CAddress object or a simpler string, as in this code:
‘ The client code can assign a regular string
pers.CurrentAddress = "Grand Plaza Hotel, Rome"
‘ or a reference to another CAddress object (requires Set).
Set pers.CurrentAddress = pers.VacationAddress
While this sort of flexibility adds a lot of power to your class, it also reduces its robustness because nothing keeps a programmer from adding a nonstring value or an object of a class other than CAddress. To be more in control of what is actually assigned to this property, you need to arbitrate all accesses to it through Property procedures. But in this case, you need three distinct Property procedures:
Private m_CurrentAddress As Variant

Property Get CurrentAddress() As Variant
If IsObject(m_CurrentAddress) Then
Set CurrentAddress = m_CurrentAddress ‘ Return a CAddress object.
Else
CurrentAddress = m_CurrentAddress ‘ Return a string.
End If
End Property

Property Let CurrentAddress(ByVal newValue As Variant)
m_CurrentAddress = newValue
End Property

Property Set CurrentAddress(ByVal newValue As Variant)
Set m_CurrentAddress = newValue
End Property
The Property Let procedure is invoked when a regular value is assigned to the property, while the Property Set procedure comes into play when the client assigns an object with a Set command. Note how the Property Get procedure returns a value to the caller code: It has to test whether the private variable currently contains an object, and it must use a Set command if it does. The Property Let and Set pair lets you enforce a better validation scheme:
Property Let CurrentAddress(ByVal newValue As Variant)
‘ Check that it is a string value.
If VarType(newValue) <> vbString Then Err.Raise 5
m_CurrentAddress = newValue
End Property

Property Set CurrentAddress(ByVal newValue As Variant)
‘ Check that it is a CAddress object.
If TypeName(newValue) <> "CAddress" Then Err.Raise 5
Set m_CurrentAddress = newValue
End Property
Here’s a technique that lets you save some code and slightly improve run-time performances. The trick is to declare the type of the object you’re expecting right in the parameter list of the Property Set procedure, as in this code:
Property Set CurrentAddress(ByVal newValue As CAddress)
Set m_CurrentAddress = newValue
End Property
You can’t use this approach in all circumstances; for example, you can’t use it when you’re willing to accept two or more objects of different types. In that case, it’s best to use an As Object parameter:
Property Set CurrentAddress(ByVal newValue As Object)
If TypeName(newValue) <> "CAddress" And TypeName(newValue) <> _
"COtherType" Then Err.Raise 5
Set m_CurrentAddress = newValue
End Property
As far as Visual Basic is concerned, the real type is determined by the value declared in the Property Get procedure. In fact, that’s the type reported in the Object Browser.

No comments:

Post a Comment