常数点运算符(VBA)

R. *_*ter 14 vba const constants

我想有一个常量材料的目录,所以我可以使用如下所示的代码:

Dim MyDensity, MySymbol
MyDensity = ALUMINUM.Density
MySymbol = ALUMINUM.Symbol
Run Code Online (Sandbox Code Playgroud)

显然铝的密度和符号预计不会改变所以我希望它们是常数但我喜欢简单的点符号.

我看到了几个选项,但我不喜欢它们.

  1. 为每种材料的每个属性制作常量.这似乎太多常数,因为我可能有20个材料,每个都有5个属性.

    Const ALUMINUM_DENSITY As Float = 169.34
    Const ALUMINUM_SYMBOL As String = "AL"
    
    Run Code Online (Sandbox Code Playgroud)
  2. 使用所有材质定义枚举并生成返回属性的函数.密度不是很明显,因为它的值是由函数返回的.

    Public Enum Material
         MAT_ALUMINUM
         MAT_COPPER
    End Enum
    
    Public Function GetDensity(Mat As Material)
        Select Case Mat
            Case MAT_ALUMINUM
                GetDensity = 164.34
        End Select
    End Function
    
    Run Code Online (Sandbox Code Playgroud)

似乎Const Structs或Const Objects不会解决这个问题,但也许我错了(甚至可能不被允许).有没有更好的办法?

Com*_*ern 12

使VBA等同于"静态类".常规模块可以具有属性,没有任何东西表明它们不能是只读的.我还将密度和符号包装在一个类型中:

'Materials.bas

Public Type Material
    Density As Double
    Symbol As String
End Type

Public Property Get Aluminum() As Material
    Dim output As Material
    output.Density = 169.34
    output.Symbol = "AL"
    Aluminum = output
End Property

Public Property Get Iron() As Material
    '... etc
End Property
Run Code Online (Sandbox Code Playgroud)

这非常接近您期望的使用语义:

Private Sub Example()
    Debug.Print Materials.Aluminum.Density
    Debug.Print Materials.Aluminum.Symbol
End Sub
Run Code Online (Sandbox Code Playgroud)

如果你在同一个项目中,你甚至可以删除显式Materials限定符(虽然我建议明确它):

Private Sub Example()
    Debug.Print Aluminum.Density
    Debug.Print Aluminum.Symbol
End Sub
Run Code Online (Sandbox Code Playgroud)


Mat*_*don 5

IMO @Comintern击中头部; 这个答案只是另一种可能的选择.


为它创建一个界面.添加一个类模块,调用它IMaterial; 该接口将正式化需要的get-only属性Material:

Option Explicit
Public Property Get Symbol() As String
End Property

Public Property Get Density() As Single
End Property
Run Code Online (Sandbox Code Playgroud)

现在打开记事本并粘贴此类标题:

VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
END
Attribute VB_Name = "StaticClass1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit
Run Code Online (Sandbox Code Playgroud)

它另存为StaticClass1.cls,并保持它在你的"需要频繁使用VBA代码文件"文件夹(制造一个,如果你没有一个!).

现在将原型实现添加到文本文件中:

VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
END
Attribute VB_Name = "Material"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit    
Implements IMaterial

Private Const mSymbol As String = ""
Private Const mDensity As Single = 0

Private Property Get IMaterial_Symbol() As String
    IMaterial_Symbol = Symbol
End Property

Private Property Get IMaterial_Density() As Single
    IMaterial_Density = Density
End Property

Public Property Get Symbol() As String
    Symbol = mSymbol
End Property

Public Property Get Density() As Single
    Density = mDensity
End Property
Run Code Online (Sandbox Code Playgroud)

将该文本文件另存为Material.cls.

现在将此类Material导入您的项目; 将其重命名为AluminiumMaterial,并填写空白:

Private Const mSymbol As String = "AL"
Private Const mDensity As Single = 169.34
Run Code Online (Sandbox Code Playgroud)

Material再次导入该类,重命名为AnotherMaterial,填写空白:

Private Const mSymbol As String = "XYZ"
Private Const mDensity As Single = 123.45
Run Code Online (Sandbox Code Playgroud)

每种材料都要冲洗和重复:每种材料只需要提供一次.

如果您正在使用Rubberduck,请将文件夹注释添加到模板文件中:

'@Folder("Materials")
Run Code Online (Sandbox Code Playgroud)

然后代码资源管理器将干净地重新组合文件夹IMaterial下的所有类Materials.

拥有"许多模块"只是VBA中的一个问题,因为VBE的Project Explorer使得它非常不方便(通过在单个"classes"文件夹下填充每个类).Rubberduck的Code Explorer不会使VBA具有命名空间,但是无论如何都可以以结构化的方式组织VBA项目.

在使用方面,您现在可以使用针对IMaterial接口编写的多态代码:

Public Sub DoSomething(ByVal material As IMaterial)
    Debug.Print material.Symbol, material.Density
End Sub
Run Code Online (Sandbox Code Playgroud)

或者,您可以从公开的默认实例(从模块的VB_PredeclaredId = True属性获取)访问get-​​only属性:

Public Sub DoSomething()
    Debug.Print AluminumMaterial.Symbol, AluminumMaterial.Density
End Sub
Run Code Online (Sandbox Code Playgroud)

您可以将默认实例传递给任何需要使用的方法IMaterial:

Public Sub DoSomething()
    PrintToDebugPane AluminumMaterial
End Sub

Private Sub PrintToDebugPane(ByVal material As IMaterial)
    Debug.Print material.Symbol, material.Density
End Sub
Run Code Online (Sandbox Code Playgroud)

好处是,您可以获得所有内容的编译时验证; 这些类型是不可能滥用的.

缺点是,您需要许多模块(类),如果接口需要更改,则需要更新许多类以保持代码可编译.

  • @MathieuGuindon我喜欢看你的帖子.使vba看起来不像是孩子的工具<3 (3认同)
  • @GSerg我知道,我相信这个答案已经清楚地说明了这一点.然而,它是如何获得100%类型安全性和编译器辅助的一致性,而不暴露可变UDT(在VBA中具有令人沮丧的限制).这是一个值得考虑的解决方案*无论OP认为有多少模块.在任何OOP语言中,拥有多个类都不是问题; 我的看法是它只是VBA中的一个问题,因为IDE*使它变得不方便.还有工具可以解决这个问题. (2认同)
  • @ R.Binter VBA没有做类无效,所以你不能有一个铝子类型,但你确实可以拥有尽可能多的类来实现你需要的`IMaterial`.IMO我们都错过了一个基本点:这是*数据*,而不是*代码*.如果代码托管在Excel中,将数据存放在(隐藏的?)工作表(或Access中的db表)中 - 那么您只需要一个接口的实现,比如,一个`Material`类,其余的代码可以写成`IMaterial`,并且所有实例都可以用`Symbol`作为键存储在`Dictionary`中,这使得检索变得微不足道. (2认同)
  • ...基本上我挑战"铝.密度"甚至需要存在的前提; 代码应该只需要了解`IMaterial`,而不需要关心它是否与`Aluminum`或`Titanium`或`Gold`一起使用.如果代码是相同的,无论你使用什么材料,很多代码都会被削减掉! (2认同)