有没有办法为具有私有成员的 VBA 类编写相等测试而不暴露这些私有成员的存在?

发布于 2024-10-08 18:00:50 字数 1003 浏览 5 评论 0原文

我进行了大量的 Excel VBA 编程,但其中并不是很多都是面向对象的。时不时就会出现一些让我烦恼的事情,我想知道我是否遗漏了一些东西。

在 VBA 中,假设我有一个类 C 定义了一些私有成员,如下所示:

'...

Private hidden1_ As Double
Private hidden2_ As Double

'...

如果 VBA 像 C++ 或(大多数?)其他支持 OOP 的语言一样工作,我可以编写一个成员函数来在类 C 的实例之间进行相等测试,例如this:

'Error: won't compile!
Public Function equal(cinst As C) As Boolean
    equal = (hidden1_ = cinst.hidden1_ And hidden2_ = cinst.hidden2_)
End Function

当然,这不会在 VBA 中编译,因为类成员函数只能访问调用它们的同一实例的私有类成员。我想到的最好的做法是定义两个成员函数,如下所示:

Public Function equalDef(hidden1 As Double, hidden2 As Double) As Boolean
    equalDef = (hidden1_ = hidden1 And hidden2_ = hidden2)
End Function

Public Function equal(cinst As C) As Boolean
    equal = cinst.equalDef(hidden1_, hidden2_)
End Function

它很麻烦,并且暴露了私有类成员存在的知识,但至少它避免了实际暴露 私有类成员的值

这是我能做的最好的事情吗?

编辑:

像往常一样,在回答之后,我意识到了一种更好的方式来表达问题。它的标题是“是否有更简洁的方法来为具有私有成员的 VBA 类编写相等性测试?”当迪克回答的时候。

I do a fair amount of Excel VBA programming, but not a lot of it is object-oriented. Here is something that comes up every now and then that bugs me, and I'm wondering if there's something I'm missing.

In VBA, say I have a class C defined with some private members like so:

'...

Private hidden1_ As Double
Private hidden2_ As Double

'...

If VBA worked like C++ or (most?) other languages that support OOP, I could write a member function to do an equality test between instances of class C like this:

'Error: won't compile!
Public Function equal(cinst As C) As Boolean
    equal = (hidden1_ = cinst.hidden1_ And hidden2_ = cinst.hidden2_)
End Function

Of course, that won't compile in VBA because class member functions can only access private class members of the same instance they are invoked on. The best I've ever come up with to do this sort of thing is to instead define two member functions like this:

Public Function equalDef(hidden1 As Double, hidden2 As Double) As Boolean
    equalDef = (hidden1_ = hidden1 And hidden2_ = hidden2)
End Function

Public Function equal(cinst As C) As Boolean
    equal = cinst.equalDef(hidden1_, hidden2_)
End Function

It's cumbersome, and it exposes knowledge of the existence of private class members, but at least it avoids actually exposing the values of private class members.

Is this the best I can do?

EDIT:

As usual, after an answer, I've realized a better way to phrase the question. It was titled "Is there a cleaner way to write an equality test for a VBA class with private members?" when Dick answered it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

生生漫 2024-10-15 18:00:50

我会像这样编写类

Private mdhidden1_ As Double
Private mdhidden2_ As Double

Public Property Get hidden1_() As Double

    hidden1_ = mdhidden1_

End Property

Public Property Get hidden2_() As Double

    hidden2_ = mdhidden2_

End Property

Private Sub Class_Initialize()

    'some method of setting variables private to the class
    mdhidden1_ = 1
    mdhidden2_ = 2

End Sub

Public Property Get IsEquivalent(clsCompare As C) As Boolean

    IsEquivalent = Me.hidden1_ = clsCompare.hidden1_ And Me.hidden2_ = clsCompare.hidden2_

End Property

如果您无论如何都被迫公开成员的知识,您不妨将其设为只读属性(Get,但不让)。然后您可以在类中创建 IsEquivalent 布尔属性。

I would write the class like this

Private mdhidden1_ As Double
Private mdhidden2_ As Double

Public Property Get hidden1_() As Double

    hidden1_ = mdhidden1_

End Property

Public Property Get hidden2_() As Double

    hidden2_ = mdhidden2_

End Property

Private Sub Class_Initialize()

    'some method of setting variables private to the class
    mdhidden1_ = 1
    mdhidden2_ = 2

End Sub

Public Property Get IsEquivalent(clsCompare As C) As Boolean

    IsEquivalent = Me.hidden1_ = clsCompare.hidden1_ And Me.hidden2_ = clsCompare.hidden2_

End Property

If you're forced to expose knowledge of the member anyway, you may as well make it a read-only property (Get, but no Let). Then you can make the IsEquivalent boolean property inside the class.

赠我空喜 2024-10-15 18:00:50

再次研究这个问题后,我有了答案,但并不完全令人满意。与 VBA 中的大多数 OOP 一样,它涉及使用适当的接口,但每个类(以及每个接口)都必须位于单独的类模块中,这一事实使其成为一种相当笨拙的执行方式。所以这里是:

假设我有一个名为 MyClass 的类(上面我将其称为“C”,但现在我将其称为“MyClass”以使这一点更清楚。)

要做的事情是定义一个我实际使用的接口在我的代码中,只公开了我想要真正公开的有关 MyClass 的内容。假设这段代码位于一个名为 IMyClass 的类模块中:

Public Function calcSomething()

End Function

Public Function equal(cinst As IMyClass) As Boolean

End Function

然后我有一个名为 MyClass 的类,在类模块中使用此代码进行定义:

Implements IMyClass

Private hidden1_ As Double
Private hidden2_ As Double

Public Sub init(h1 As Double, h2 As Double)
    hidden1_ = h1
    hidden2_ = h2
End Sub

Public Function equalDef(hidden1 As Double, hidden2 As Double) As Boolean
    equalDef = (hidden1_ = hidden1 And hidden2_ = hidden2)
End Function

Private Function IMyClass_calcSomething() As Variant
    IMyClass_calcSomething = hidden1_ * hidden2_
End Function

Private Function IMyClass_equal(cinst As IMyClass) As Boolean
    If TypeOf cinst Is MyClass Then
        Dim asMyClass As MyClass
        Set asMyClass = cinst

        IMyClass_equal = asMyClass.equalDef(hidden1_, hidden2_)
    End If
End Function

下面是一些将放入常规模块中的代码:

Public Function mkMyClass(h1 As Double, h2 As Double) As IMyClass
    Dim ret As MyClass
    Set ret = New MyClass

    Call ret.init(h1, h2)

    Set mkMyClass = ret
End Function

Public Sub useMyClass()
    Dim mc1 As IMyClass
    Set mc1 = mkMyClass(42, 99)

    Dim mc2 As IMyClass
    Set mc2 = mkMyClass(42, 99)

    Dim mc3 As IMyClass
    Set mc3 = mkMyClass(99, 42)

    Debug.Print mc1.calcSomething
    Debug.Print mc1.equal(mc2)

    Debug.Print mc3.calcSomething
    Debug.Print mc3.equal(mc2)
End Sub

在 'useMyClass' 例程中,您可以验证各个 mc1、mc2 和 mc3 变量看不到 MyClass 的 'init' 或 'equalDef' 方法。他们只能看到属于接口一部分的“calcSomething”和“equal”方法。

所以,我想,这个问题是正式提出的,但答案并不令人满意。至少它让我有机会重复“VBA 中的 OOP 是 PITA (TM)”...

以下是一些相关的 stackoverflow 答案:

VBA继承,super的模拟

有没有办法重载 VBA 中类的构造函数/初始化过程?

After looking into this again, I have an answer, but it's not exactly satisfying. As with most OOP in VBA, it involves using an appropriate interface, but the fact that every class (and every interface) has to go in a separate class module makes it a pretty clunky way to do things. So here goes:

Say I have a class called MyClass (which I was calling 'C' above but I'm now calling 'MyClass' to make this clearer.)

The thing to do would be to define an interface that I would actually use in my code that exposed just the things about MyClass that I wanted truly public. Say this code is in a class module called IMyClass:

Public Function calcSomething()

End Function

Public Function equal(cinst As IMyClass) As Boolean

End Function

Then I have my class called MyClass, defined with this code in a class module:

Implements IMyClass

Private hidden1_ As Double
Private hidden2_ As Double

Public Sub init(h1 As Double, h2 As Double)
    hidden1_ = h1
    hidden2_ = h2
End Sub

Public Function equalDef(hidden1 As Double, hidden2 As Double) As Boolean
    equalDef = (hidden1_ = hidden1 And hidden2_ = hidden2)
End Function

Private Function IMyClass_calcSomething() As Variant
    IMyClass_calcSomething = hidden1_ * hidden2_
End Function

Private Function IMyClass_equal(cinst As IMyClass) As Boolean
    If TypeOf cinst Is MyClass Then
        Dim asMyClass As MyClass
        Set asMyClass = cinst

        IMyClass_equal = asMyClass.equalDef(hidden1_, hidden2_)
    End If
End Function

And here is some code that would go in a regular module:

Public Function mkMyClass(h1 As Double, h2 As Double) As IMyClass
    Dim ret As MyClass
    Set ret = New MyClass

    Call ret.init(h1, h2)

    Set mkMyClass = ret
End Function

Public Sub useMyClass()
    Dim mc1 As IMyClass
    Set mc1 = mkMyClass(42, 99)

    Dim mc2 As IMyClass
    Set mc2 = mkMyClass(42, 99)

    Dim mc3 As IMyClass
    Set mc3 = mkMyClass(99, 42)

    Debug.Print mc1.calcSomething
    Debug.Print mc1.equal(mc2)

    Debug.Print mc3.calcSomething
    Debug.Print mc3.equal(mc2)
End Sub

In the 'useMyClass' routine, you can verify that the various mc1, mc2, and mc3 variables can't see the 'init' or 'equalDef' methods of MyClass. They can only see the 'calcSomething' and 'equal' methods that are part of the interface.

So, question officially but unsatisfyingly answered, I guess. At least it gives me a chance to repeat "OOP in VBA is a PITA (TM)"...

Here are some related stackoverflow answers:

VBA inheritance, analog of super

Is there a way to overload the constructor / initialize procedure for a class in VBA?

过期情话 2024-10-15 18:00:50

我知道这是一篇旧帖子,但我仍然想回答。以下代码不向用户公开任何私有变量,并且仍然允许在对象之间进行比较:

'MyClass
Private m_data As Double
Private Const epsilon As Double = 0.00001


Public Function IsEqual(ByRef cls As MyClass) As Boolean
    IsEqual = cls.Comparator(m_data)
End Function

Public Function Comparator(ByVal d As Double) As Boolean
    Comparator = Abs((d - m_data)) < epsilon
End Function

Property Get MyString() As String
    MyString = Chr((CLng(m_data) Mod 26) + 65) & Chr((CLng(m_data * 100) Mod 26) + 65)
End Property

Property Let MyString(s As String)
    If Len(s) = 0 Then
        m_data = epsilon / 10
        Exit Property
    End If
    m_data = Len(s) / (Len(s) - Len(Replace(s, " ", "")))
End Property

'Module
Public Sub mySub2()
Dim s As String
Dim obj1 As MyClass
Dim obj2 As MyClass

    Set obj1 = New MyClass
    Set obj2 = New MyClass

    s = InputBox("Enter a sentence:")
    Debug.Print s

    obj1.MyString = s
    obj2.MyString = ""

    Debug.Print "Does Obj1(" & obj1.MyString & ") = Obj2(" & obj2.MyString & ")?  " & obj1.IsEqual(obj2)

    obj2.MyString = s

    Debug.Print "Does Obj1(" & obj1.MyString & ") = Obj2(" & obj2.MyString & ")?  " & obj1.IsEqual(obj2)

End Sub

具有以下输出:

The quick brown fox jumps over the lazy moon
Does Obj1(GE) = Obj2(AA)?  False
Does Obj1(GE) = Obj2(GE)?  True

I know this is an old post, but I would still like to answer. The following code exposes no private variables to the user and still allows comparison between objects:

'MyClass
Private m_data As Double
Private Const epsilon As Double = 0.00001


Public Function IsEqual(ByRef cls As MyClass) As Boolean
    IsEqual = cls.Comparator(m_data)
End Function

Public Function Comparator(ByVal d As Double) As Boolean
    Comparator = Abs((d - m_data)) < epsilon
End Function

Property Get MyString() As String
    MyString = Chr((CLng(m_data) Mod 26) + 65) & Chr((CLng(m_data * 100) Mod 26) + 65)
End Property

Property Let MyString(s As String)
    If Len(s) = 0 Then
        m_data = epsilon / 10
        Exit Property
    End If
    m_data = Len(s) / (Len(s) - Len(Replace(s, " ", "")))
End Property

'Module
Public Sub mySub2()
Dim s As String
Dim obj1 As MyClass
Dim obj2 As MyClass

    Set obj1 = New MyClass
    Set obj2 = New MyClass

    s = InputBox("Enter a sentence:")
    Debug.Print s

    obj1.MyString = s
    obj2.MyString = ""

    Debug.Print "Does Obj1(" & obj1.MyString & ") = Obj2(" & obj2.MyString & ")?  " & obj1.IsEqual(obj2)

    obj2.MyString = s

    Debug.Print "Does Obj1(" & obj1.MyString & ") = Obj2(" & obj2.MyString & ")?  " & obj1.IsEqual(obj2)

End Sub

With the following output:

The quick brown fox jumps over the lazy moon
Does Obj1(GE) = Obj2(AA)?  False
Does Obj1(GE) = Obj2(GE)?  True
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文