如何在VBA中以类似于VB.NET的方式使用类对象模块之间的比较方法?

24

由于在 VBA 中有一个新项目,我从 VB.NET 转移过来,老实说我不太知道如何处理对象类之间的关系。我的目标是比较不同类别对象模块之间的对象。

例如:

class Employee
     属性:NameAge
     重点是:比较两个员工的 Name

类别为:EmployeeManager
重点是:比较 EmployeeNameManagerName

我知道如何在 VB.NET 中实现,但我该如何在 VBA 中比较不同类模块对象的属性?

1个回答

82
VBA不支持类多态性,因此建议您改变对员工和经理类的思考方式。您不能将一个员工类作为基类,然后再有一个单独的经理类从员工类派生。它们可以是两个实现共同接口的分离类。稍后我将详细讨论这个问题。现在让我们通过一些例子来说明。

↓ 简单方法 ↓


一个 base 类(Person),以及从该基类派生的子类。(适用于C#、VB.NET等)

但在VBA中,您需要这样思考:

一个公开枚举属性描述位置的基类。

类似于:

enter image description here

enter image description here

这是暴露一些属性的类最简单的方法。它允许您将Person对象添加到集合中,并使用易于使用的for each循环和Intellisense进行迭代!

enter image description here

属性比较系统将会非常非常容易。

enter image description here

注意:枚举类型同样会被隐式转换为数字。

↓ 更复杂的方法 ↓


有两个不同的类,它们都公开了属性。例如,您有一个名为 Employee 和一个名为 Manager 的类,它们都实现了一个 Person 接口 和一个额外的 Comparer 类,该类公开了一个 Compare() 方法。

enter image description here

在您的VBA项目中,您需要4个类模块和一个标准模块。

enter image description here

Person (这是您的接口)

Public Property Get Name() As String
End Property

Public Property Get Age() As Long
End Property

这个类是接口,EmployeeManager都需要实现它以共享一些通用函数(获取名称和年龄的getter)。拥有接口允许您使用接口类型变量作为枚举器进行for each循环。您马上会看到。

EmployeeManager是相同的。显然,您可以根据实际情况修改它们以适应您的解决方案。

Implements Person

Private name_ As String
Private age_ As Long

Public Property Get Name() As String
    Name = name_
End Property

Public Property Let Name(ByVal Value As String)
    name_ = Value
End Property

Public Property Get Age() As Long
    Age = age_
End Property

Public Property Let Age(ByVal Value As Long)
    age_ = Value
End Property

Private Property Get Person_Name() As String
    Person_Name = Name
End Property

Private Property Get Person_Age() As Long
    Person_Age = Age
End Property

ComparerCls 你将使用此类的实例来比较两个对象的属性或引用。您不一定需要为此创建一个类,但我更喜欢这样做。

Public Enum ComparisonMethod
    Names = 0 ' default
    Ages = 1
    References = 2
End Enum

' makes names the default comparison method
Public Function Compare(ByRef obj1 As Person, _
                        ByRef obj2 As Person, _
                        Optional method As ComparisonMethod = 0) _
                        As Boolean

    Select Case method
        Case Ages
            Compare = IIf(obj1.Age = obj2.Age, True, False)
        Case References
            Compare = IIf(obj1 Is obj2, True, False)
        Case Else
            Compare = IIf(obj1.Name = obj2.Name, True, False)
    End Select

End Function

和您的Module1代码

Option Explicit

Sub Main()

    Dim emp As New Employee
    emp.Name = "person"
    emp.Age = 25

    Dim man As New Manager
    man.Name = "manager"
    man.Age = 25

    Dim People As New Collection
    People.Add emp
    People.Add man

    Dim individual As Person
    For Each individual In People
        Debug.Print TypeName(individual), individual.Name, individual.Age
    Next

End Sub

运行Main()子程序并在立即窗口中查看结果。

enter image description here

上述代码的最佳部分是您正在创建一个实现Person接口的引用变量。这允许您循环遍历集合中实现该接口的所有项。此外,如果有更多的属性和函数,您可以使用Intellisense,这非常棒。

比较


再次查看ComparerCls类中的代码

enter image description here

我希望你现在能理解为什么我要将此分离为一个类。它的目的只是处理对象比较的方式。你可以指定枚举顺序并修改 Compare() 方法本身以不同的方式进行比较。请注意可选参数,它允许您调用 Compare 方法而无需比较方法。

enter image description here

现在,您可以尝试传递不同的参数到比较函数中,并查看结果。请尝试不同的组合:
emp.Name = "name"
man.Name = "name"

Comparer.Compare(emp, name, Names)
Comparer.Compare(emp, name, References)

Comparer.Compare(emp, emp, References)

如果有什么不清楚的地方,请参考VBA中Implements关键字的解释

1
非常好的例子,感谢您的时间和贡献。但由此引发了两个问题。我可以看到Person是一个接口,其他类可以实现它,但在这种情况下,Person实际上也是CLASS,其他类从中实现,例如Manager和Employee在示例中(两个属性),除了属性之外,它们也可以是方法吗?其次,如果可能的话,Manager类可以实现Person(class)接口,为什么不能让Manager实现Employee的方法和字段,因为它们与Person相同? - Arie
1
@StackUser 嗯,是的,请看这个链接 ,里面有其他类实现接口时需要提供方法实现的示例。我认为第二个问题的答案就在我的回答前几句话中。VBA不支持类多态性,这就是为什么一个类不能从另一个类派生的原因。 - user2140173
1
@StackUser 如果仍有不清楚的地方,也许可以阅读这个答案:这里 :) - user2140173
5
+1 太棒了的回答!非常有用!我不知道我们可以在VBA中使用接口! - Dmitry Pavliv
很好的答案。我不理解的是Employee和Manager类中的私有“Person_Name”和“Person_Age”属性。它们在任何地方使用吗? - user1965813
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接