带点运算符的常数(VBA)

时间:2018-11-16 17:11:25

标签: vba const constants

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

T

很明显,铝的密度和符号不会发生变化,因此我希望它们为常数,但为简单起见,我喜欢点符号。

我看到了一些选择,但我不喜欢它们。

  1. 为每种材料的每个属性赋予常数。这似乎太多了,因为我可能有20种材料,每种材料都有5个属性。

    Dim MyDensity, MySymbol
    MyDensity = ALUMINUM.Density
    MySymbol = ALUMINUM.Symbol
    
  2. 用所有材料定义一个枚举,并创建返回属性的函数。密度不变不是很明显,因为它的值是由一个函数返回的。

    Const ALUMINUM_DENSITY As Float = 169.34
    Const ALUMINUM_SYMBOL As String = "AL"
    

看起来好像Const Structs或Const Objects并不能解决这个问题,但是也许我错了(甚至可能不允许这样做)。有更好的方法吗?

5 个答案:

答案 0 :(得分: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

这非常接近您所需的用法语义:

Private Sub Example()
    Debug.Print Materials.Aluminum.Density
    Debug.Print Materials.Aluminum.Symbol
End Sub

如果您在同一个项目中,甚至可以删除显式Materials限定词(尽管我建议将其定为显性):

Private Sub Example()
    Debug.Print Aluminum.Density
    Debug.Print Aluminum.Symbol
End Sub

答案 1 :(得分:5)

IMO @Comintern撞到了头上的钉子;这个答案只是另一个可能的选择。


为其创建接口。添加一个类模块,将其命名为IMaterial;该接口将使Material需要的只读属性形式化:

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

Public Property Get Density() As Single
End Property

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

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

将其另存为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

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

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

Private Const mSymbol As String = "AL"
Private Const mDensity As Single = 169.34

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

Private Const mSymbol As String = "XYZ"
Private Const mDensity As Single = 123.45

冲洗并重复每种材料:每个材料只需提供一次值。

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

'@Folder("Materials")

然后 Code Explorer 将干净地重新分组IMaterial文件夹下的所有Materials类。

在VBA中只有“很多模块”是一个问题,因为VBE的 Project Explorer 使其非常不方便(通过将每个单独的类填充在单个“ classes”文件夹下)。 Rubberduck的代码资源管理器不会使VBA具有名称空间,但是无论如何都可以使您以结构化的方式组织VBA项目。

明智地,您现在可以针对IMaterial接口编写多态代码:

Public Sub DoSomething(ByVal material As IMaterial)
    Debug.Print material.Symbol, material.Density
End Sub

或者您可以从公开的默认实例(您从模块的VB_PredeclaredId = True属性获得的)访问只读属性:

Public Sub DoSomething()
    Debug.Print AluminumMaterial.Symbol, AluminumMaterial.Density
End Sub

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

Public Sub DoSomething()
    PrintToDebugPane AluminumMaterial
End Sub

Private Sub PrintToDebugPane(ByVal material As IMaterial)
    Debug.Print material.Symbol, material.Density
End Sub

另一方面,您可以获得所有内容的编译时验证;这些类型是不可能滥用的。

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

答案 2 :(得分:2)

您可以创建一个名为“ ALUMINIUM”的模块,并将以下内容放入其中:

Public Const Density As Double = 169.34
Public Const Symbol As String = "AL"

现在在另一个模块中,您可以像这样调用它们:

Sub test()
    Debug.Print ALUMINUM.Density
    Debug.Print ALUMINUM.Symbol
End Sub

答案 3 :(得分:2)

您可以创建一个Class模块-我们将其称为Material(材质),并定义材质作为公共成员(变量)具有的属性,例如Density,Symbol:

Public Density As Float
Public Symbol As String

然后在标准模块中创建材料:

Public Aluminium As New Material
Aluminium.Density = 169.34
Aluminium.Symbol = "AL"

Public Copper As New Material
' ... etc

添加行为

关于类的好处是,您可以在其中定义函数(方法),也可以在任何实例上使用点符号来调用它们。例如,如果可以在类中定义:

Public Function AsString() 
    AsString = Symbol & "(" & Density & ")"
End Function

...然后使用您的实例Aluminium(请参见前面的内容),可以执行以下操作:

MsgBox Aluminium.AsString() ' =>   "AL(169.34)"

每当您要实现所有材料都必须具有的新功能/行为时,只需在类中实现它即可。

另一个例子。在类中定义:

Public Function CalculateWeight(Volume As Float) As Float
    CalculateWeight = Volume * Density
End Function 

...,您现在可以这样做:

Weight = Aluminium.CalculateWeight(50.6)

将属性设置为只读

如果要确保您的代码没有为DensitySymbol属性分配新值,则需要更多代码。在类中,您将使用getter和setter定义这些属性(使用GetSet语法)。例如,Symbol的定义如下:

Private privSymbol as String

Property Get Symbol() As String
    Symbol = privSymbol
End Property

Property Set Symbol(value As String)
    If privSymbol = "" Then privSymbol = value
End Property

上面的代码仅允许设置Symbol属性与空字符串不同。设置为“ AL”后,将无法再进行更改。如果尝试这样,您甚至可能会提出一个错误。

答案 4 :(得分:0)

我喜欢混合方法。这是伪代码,因为我没有足够的时间来完成示例。

创建MaterialsDataClass-请参阅Mathieu Guindon关于将其设置为静态类的知识

Private ArrayOfSymbols() as String
Private ArrayOfDensity() as Double
Private ArrayOfName() as String
' ....

    ArrayOfSymbols = Split("H|He|AL|O|...","|")
    ArrayOfDensity = '....
    ArrayOfName = '....

    Property Get GetMaterialBySymbol(value as Variant) as Material
    Dim Index as Long
    Dim NewMaterial as Material
        'Find value in the Symbol array, get the Index
        New Material = SetNewMaterial(ArrayOfSymbols(Index), ArrayofName(Index), ArrayofDensity(Index))
        GetMaterialBySymbol = NewMaterial
    End Property

Property Get GetMaterialByName(value as string) ' etc.

Material本身与其他答案类似。我在下面使用了Type,但是与Class相比,我更喜欢Type,因为它们可以提供更多功能,并且还可以在“ For Each”循环中使用。

Public Type Material
    Density As Double
    Symbol As String
    Name as String
End Type

您的用法:

Public MaterialsData as New MaterialsDataClass
Dim MyMaterial as Material
    Set MyMaterial = MaterialsDataClass.GetMaterialByName("Aluminium")
    Debug.print MyMaterial.Density