Константа с оператором точки (VBA) - PullRequest
0 голосов
/ 16 ноября 2018

Я хочу иметь каталог постоянных материалов, чтобы я мог использовать код, который выглядит следующим образом:

Dim MyDensity, MySymbol
MyDensity = ALUMINUM.Density
MySymbol = ALUMINUM.Symbol

Очевидно, что плотность и символ для алюминия не должны изменяться, поэтому я хочу, чтобы они были постоянными, но мне нравится точечная запись для простоты.

Я вижу несколько вариантов, но они мне не нравятся.

  1. Создайте константы для каждого свойства каждого материала. Это кажется слишком большим количеством констант, так как у меня может быть 20 материалов, каждый из которых имеет 5 свойств.

    Const ALUMINUM_DENSITY As Float = 169.34
    Const ALUMINUM_SYMBOL As String = "AL"
    
  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
    

Не похоже, что Const Structs или Const Objects решат эту проблему, но, возможно, я ошибаюсь (их даже нельзя допустить). Есть ли лучший способ?

Ответы [ 5 ]

0 голосов
/ 17 ноября 2018

Мне нравится гибридный подход.Это псевдокод, потому что у меня нет времени, чтобы полностью проработать пример.

Создать MaterialsDataClass - см. Знания Матье Гиндона о том, как настроить его как статический класс

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.
*Сам 1006 *Material похож на другие ответы.Я использовал Type ниже, но я предпочитаю Class es вместо Type s, потому что они предоставляют больше функциональных возможностей, и их также можно использовать в циклах «For Each».
Public Type Material
    Density As Double
    Symbol As String
    Name as String
End Type

Inваше использование:

Public MaterialsData as New MaterialsDataClass
Dim MyMaterial as Material
    Set MyMaterial = MaterialsDataClass.GetMaterialByName("Aluminium")
    Debug.print MyMaterial.Density
0 голосов
/ 16 ноября 2018

ИМО @ Коминтерн удар ногтем по голове;Этот ответ является еще одной возможной альтернативой.


Создайте интерфейс для него.Добавьте модуль класса, назовите его 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"). Code Explorer от Rubberduck не даст VBA иметь пространства имен, но позволяет структурировать ваш проект VBA независимо от того.

С точки зрения использования теперь вы можете написать полиморфный код для *Интерфейс 1054 *:

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

С другой стороны, вы получаете проверку во время компиляции для всего;неправильно использовать типы.

Недостатки: вам нужно много модулей (классов), и если нужно изменить интерфейс, это приводит к обновлению большого количества классов, чтобы обеспечить возможность компиляции кода.

0 голосов
/ 16 ноября 2018

Сделать 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
0 голосов
/ 16 ноября 2018

Вы можете создать модуль 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, тогда вам нужно немного больше кода.В классе вы определяете эти свойства с помощью методов получения и установки (используя синтаксис 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» его уже нельзя изменить.Возможно, вы даже захотите вызвать ошибку, если такая попытка будет предпринята.

0 голосов
/ 16 ноября 2018

Вы можете создать модуль с именем "ALUMINUM" и поместить в него следующее:

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
...