我想要一个常量材料的目录,以便可以使用如下代码:
T
很明显,铝的密度和符号不会发生变化,因此我希望它们为常数,但为简单起见,我喜欢点符号。
我看到了一些选择,但我不喜欢它们。
为每种材料的每个属性赋予常数。这似乎太多了,因为我可能有20种材料,每种材料都有5个属性。
Dim MyDensity, MySymbol
MyDensity = ALUMINUM.Density
MySymbol = ALUMINUM.Symbol
用所有材料定义一个枚举,并创建返回属性的函数。密度不变不是很明显,因为它的值是由一个函数返回的。
Const ALUMINUM_DENSITY As Float = 169.34
Const ALUMINUM_SYMBOL As String = "AL"
看起来好像Const Structs或Const Objects并不能解决这个问题,但是也许我错了(甚至可能不允许这样做)。有更好的方法吗?
答案 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)
如果要确保您的代码没有为Density
和Symbol
属性分配新值,则需要更多代码。在类中,您将使用getter和setter定义这些属性(使用Get
和Set
语法)。例如,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