首页 > 解决方案 > 是否可以在 VBA 中使用不同的数据类型进行 Let 和 Get?

问题描述

我目前正在开发一个 Excel VBA 加载项,以将一个系统的 CSV 导出处理为利益相关者希望查看的格式。我对 OOP 还很陌生,但我认为可以转换优先级在 Let 代码中输入类型,并以与输出相同的格式存储数据。

我写了以下课程:

'Class Module "Project"
Option Explicit

Private m_sPriority As String

Property Let Priority(lInput As Long)
    Select Case lInput
        Case 1
            m_sPriority = "High"
        Case 2
            m_sPriority = "Medium"
        Case 3
            m_sPriority = "Low"
        Case Else
            m_sPriority = "No Priority"
    End Select
End Property

Property Get Priority() As String
    Priority = m_sPriority
End Property

这是测试它的模块:

'Standard Module
Option Explicit

Sub test()
        Dim Project As Project
        Set Project = New Project

        Project.Priority = 1
        Debug.Print Project.Priority
End Sub

我希望在运行它时看到控制台的“高”输出。

有没有办法在 VBA 中实现这一点,或者,我的代码是否格式错误?

标签: excelvbaclassoop

解决方案


是的,您的代码格式错误。不管它值多少钱,你也不能在 .NET 中这样做——无论是在 C# 中,语法都使它相当明确:

private string _priority;
public string Priority 
{
    get { return _priority; }
    private set { _priority = value; }
}

...或在 VB 中:

Private _priority As String
Public Property Priority() As String
    Get
        Return _priority
    End Get
    Private Set(ByVal value As String)
        _priority = value
    End Set
End Property

属性要么是 a 要么是Longa String,不能同时是两者。Get//访问LetSet必须是一致的,在VBA中也是如此。

你可以通过失去类型安全和早期绑定来作弊,通过使你的属性成为......但这并不是开始使用 OOP 的好方法;-)Variant

Public Enum PriorityLevel
    NoPriority = 0
    HighPriority
    MediumPriority
    LowPriority
End Enum

Private Type InstanceState
    Priority As PriorityLevel
    '...
End Type

Private this As InstanceState

Public Property Get Priority() As Variant
    Priority = PriorityName(this.Priority)
End Property

Public Property Let Priority(ByVal value As Variant)
    this.Priority = value
End Property

Private Function PriorityName(ByVal value As PriorityLevel) As String
    Select Case value
        Case HighPriority
            PriorityName = "High"
        Case MediumPriority
            PriorityName = "Medium"
        Case LowPriority
            PriorityName= "Low"
        Case Else
            PriorityName= "Undefined"
    End Select
End Function

虽然这工作得非常好并且表面上看起来很整洁,但在你还没有编写它的时候使用这个类肯定会令人惊讶:如果你设置( Let) a Long,你也理所当然地期望Geta Long。这个类正在乞求一个PriorityNameget-only 属性:

Public Property Get Priority() As PriorityLevel
    Priority = this.Priority
End Property

Public Property Let Priority(ByVal value As PriorityLevel)
    this.Priority = value
End Property

Public Property Get PriorityName() As String
    Select Case this.Priority
        Case HighPriority
            PriorityName = "High"
        Case MediumPriority
            PriorityName = "Medium"
        Case LowPriority
            PriorityName = "Low"
        Case Else
            PriorityName = "Undefined"
    End Select
End Property

现在,早期绑定和类型安全(无论您在 VBA 中利用多少)的好处立即显而易见:当您编写分配Priority此类型对象属性的代码时,该类型的IntelliSenseEnum指导编写表达:

可用的枚举成员出现在下拉列表中

此外,它Enum正在抽象底层数值,这变得无关紧要:1代码现在说的是 ,而不是魔法硬编码HighPriority。此外,Get访问器尽可能简单,这非常好:Get访问器永远不应该引发任何错误,所以越简单越好。

故事的精神:如果可以帮助,请不要绕过类型安全,尽一切可能使代码保持早期绑定 -尽可能避免Object;在任何地方都可以转换为已知的类/接口VariantObject

例如更喜欢这个:

Dim sheet As Worksheet
' Workbook.Worksheets(index) returns Object; casting it to Worksheet
Set sheet = ActiveWorkbook.Worksheets(1)
sheet.Range("A1").Value = 42

对此:

' "Range("A1").Value" is entirely late-bound. Beware of typos!
ActiveWorkbook.Worksheets(1).Range("A1").Value = 42

推荐阅读