这是通过伪继承我的列表来实现Mat的毕多尼表。继承不是构建在VBA中的,但是可以通过组合继承的类并模仿它的所有公共方法和成员来模拟它。因为基类的修改需要传播到所有子类,所以它是一个PITA。
实现很简单,只需添加一个私有字符串数据成员pTypedName,并且每当客户端代码试图添加TypeName(Element) <> pTypedName引发类型错误的元素时。
Private this As List
Private pTypedName As String键入要添加到列表中的元素。如果尚未设置类型,则让此元素设置类型名。否则,如果错误类型不正确,则引发错误。
Private Sub TypeCheck(ByVal element As Variant, ByVal source As String)
If pTypedName = vbNullString Then pTypedName = TypeName(element)
If (TypeName(element) <> pTypedName) Then RaiseTypeError element, source
End Sub因此
Dim tList as New TypedList
tList.Append "A"是有效的,现在tList是String类型。
在允许任何元素进入列表之前,我们还希望检查序列中的每个元素。如果我们的字符串列表是用Array("a", "b", "C", 2)扩展的,我们不希望任何元素进入这个列表。
Private Sub TypeCheckSequence(ByVal sequence As Variant, ByVal source As String)
Dim element As Variant
For Each element In sequence
TypeCheck element, source
Next element
End Sub以上两种方法都是在我们的TypedList中设置数据的任何方法开始时的锅炉板代码。
这是在失败时引发的错误。
Private Sub RaiseTypeError(ByVal badItem As Variant, ByVal method As String)
Err.Raise 13, method, "Element is of type " & TypeName(badItem) & _
", not " & TypedName & "."
End Sub注意,我们使用this.NewEnum而不是this.[_NewEnum],就像在List类中一样。
Public Property Get NewEnum() As IUnknown
Attribute NewEnum.VB_UserMemId = -4
Set NewEnum = this.NewEnum
End Property客户端代码可以问TypedList它的类型是什么,并且只有当列表为空时才设置类型。
Public Property Get TypedName() As String
TypedName = pTypedName
End Property
Public Property Let TypedName(ByVal typeName_ As String)
If this.Count > 0 Then
Err.Raise 9, TypeName(Me) & ".TypedName", _
"Can only set typename of an empty list."
End If
pTypedName = typeName_
End Property在这之后,这是相当无聊和多余的。我希望我没必要把这些都写出来。
使用类型检查BoilerPlate代码模拟所有入口点。
Public Property Let Item(ByVal index As Long, ByVal element As Variant)
Attribute Item.VB_UserMemId = 0
TypeCheck element, TypeName(Me) & ".Item"
this.Item(index) = element
End Property
Public Property Set Item(ByVal index As Long, ByVal element As Variant)
Attribute Item.VB_UserMemId = 0
TypeCheck element, TypeName(Me) & ".Item"
Set this.Item(index) = element
End Property
Public Property Let Slice(ByVal a As Long, ByVal b As Long, ByVal s As Integer, ByVal sequence As Variant)
TypeCheckSequence sequence, TypeName(Me) & ".Slice"
this.Slice(a, b, s) = sequence
End PropertyPublic Sub Append(ByVal element As Variant)
TypeCheck element, TypeName(Me) & ".Append"
this.Append element
End Sub
Public Sub Extend(ByVal sequence As Variant)
TypeCheckSequence sequence, TypeName(Me) & ".Extend"
this.Extend sequence
End SubPublic Sub Emplace(ByVal index As Long, ByVal element As Variant)
TypeCheck element, TypeName(Me) & ".Emplace"
this.Emplace index, element
End Sub
Public Sub Insert(ByVal index As Long, ByVal sequence As Variant)
TypeCheckSequence sequence, TypeName(Me) & ".Insert"
collec.Insert sequence, index
End Sub现在它只是模仿所有其他的方法。甚至更无聊..。
Public Property Get Item(ByVal index As Long) As Variant
Attribute Item.VB_UserMemId = 0
seq.Assign Item, this.Item(index)
End Property
Public Property Get Slice(ByVal a As Long, ByVal b As Long, ByVal s As Integer) As List
Set Slice = this.Slice(a, b, s)
End PropertyPublic Sub Remove(ByVal index As Long)
this.Remove index
End Sub
Public Sub Clear(ByVal start As Long, ByVal size As Long)
this.Clear start, size
End SubPublic Function Count() As Long
Count = this.Count
End Function
Public Function Exists(ByVal sought As Variant) As Boolean
Exists = this.Exists(sought)
End Function
Public Function ToString() As String
ToString = this.ToString
End Function发布于 2014-09-27 14:50:59
总之,我觉得这个看起来不错。如果没有适当的继承,你就会被那些只需调用私人的List's属性的锅炉板属性所困扰。关于这一点,一切都做对了,vba就是那样糟糕。
我唯一真正注意到的是,我不喜欢你用神奇的数字来增加错误的方式。
Err.Raise 9, TypeName(Me) & ".TypedName", \_ "Can only set typename of an empty list."
和
私有子RaiseTypeError(ByVal badItem作为变体,ByVal方法作为字符串) Err.Raise 13,方法,元素类型为“& TypeName(badItem) & ",而不是”& TypedName &“。结束子对象
首先,我发现在一个地方使用潜艇是一致的,而在另一个地方则是直接提升。我知道您现在只在一个地方使用错误#9,但是将其删除肯定会使将来在其他地方使用它更容易。
但我提到了神奇的数字不是吗?通常,我建议定义一些模块作用域常量,以明确代码正在引发下标超出范围和类型失配错误(分别),但这些都是在我们正在引发的VB错误中生成的。我们可能会在许多地方使用这些模块。有鉴于此,我认为创建一个带有公共VbErrorNumbers Enum的模块是绝对值得的。这是一项工作,但你只需要做一次。事实上,我相信今天晚些时候我会亲自动手。在这里可以找到内置的VBA错误的完整列表.在这里可以找到内置的VBA错误的完整列表..
发布于 2014-09-27 15:17:56
我能补充的一点是@RubberDuck没有提到的一点是,使用this标识符来引用封装的List可能会让人困惑.但是,可能只是因为我有一个使用this的约定来引用一种私有类型,该类型将实例的私有字段合并到单个实体下;我会将其称为类似于encapsulated或underlying的类型。不过没什么大不了的。
我喜欢你拒绝一个数组/序列,其中一个元素不是“类型安全”,它非常整洁。
https://codereview.stackexchange.com/questions/64004
复制相似问题