VBA:何时使用函数与将代码放入主子

时间:2019-04-27 08:39:01

标签: excel vba readability code-readability

我正在尝试自动化将来自不同文件的季度数据编译到单个Excel工作簿中的过程(这需要每个季度完成一次)。现在,我想知道是否应该将所有代码保留在main子目录中,还是应该使用函数。我真的很想听听在VBA工作了几个月以上的人的信息,并学习如何编写高效且可读的代码。

我的代码要做的第一件事是让我获得三个与日期/日期相关的变量:

  • 当前年份,如果是第四季度数据,则为前一年
  • 取决于季度,一个变量如下所示:2018年第四季度为2018年12月31日;对于2019年第一季度将是03.31.2019。保存相关文件的位置是子文件夹名称。
  • 季度,因此Q1,Q2,Q3或Q4。

请参阅下面的代码,如果有什么需要注意的地方,请对其进行改进。具体来说,我想知道如何命名变量(如果您有一个存储年份的变量,您将如何称呼它?)。另外,Case Else不是必需的,对吗?我的意思是month(Date)只能是12个月之一。

Sub DetermineDate()

Dim qVar As String
Dim yVar As Integer
Dim fullDate As String

yVar = Year(Date) 'set value here or each time in case statement?

Select Case month(Date)
    Case 1, 2, 3
        qVar = "Q4"
        yVar = Year(Date) - 1
        fullDate = "12.31." & yVar
    Case 4, 5, 6
        qVar = "Q1"
        fullDate = "03.31." & yVar
    Case 7, 8, 9
        qVar = "Q2"
        fullDate = "06.30." & yVar
    Case 10, 11, 12
        qVar = "Q3"
        fullDate = "09.30." & yVar
    Case Else 
        MsgBox "Error"
        Exit Sub

End Select
End Sub

但是,我的问题是:是否有任何理由不将其放在主子目录中?我的想法是,最好集中精力于主子代码中的代码(复制粘贴所有数据)。因此,我认为将代码确定日期放在单独的函数中会很有意义。但是,我发现返回多个值不是直接的,也不是函数的意图。另外,我只会一次调用此函数,因此经过一番思考后我得出结论,将其放在其他位置可能没有任何意义。基本上,我想问的是使用函数和多个子对象时的最佳做法。

1 个答案:

答案 0 :(得分:2)

您可以执行以下操作。

在VBA编辑器中将以下代码添加到新的 CLASS MODULE 中,并确保为该新类命名为 clsObject ...

Public Quarter As Integer
Public Year As Integer
Public EndDate As Date

Property Get EndMonth() As Integer
    EndMonth = Month(EndDate)
End Property

Property Get QuarterText() As String
    QuarterText = "Quarter " & Quarter
End Property

Property Get DateString() As String
    DateString = Right("0" & EndMonth, 2) & "." & Day(EndDate) & "." & Year
End Property

现在,将以下代码添加到VBA编辑器中的新 MODULE 中...

Function DetermineDate(ByVal dtDate As Date) As clsObject
    ' Initialise the current functions return value.
    ' This is only required if the return value is an object that is yet to be assigned a value
    Set DetermineDate = New clsObject

    ' If the year is a simple derivation that is not specific to the month, do it here.
    ' You can also set it here first if it is the default for the vast majority of cases
    ' and then in the case statement, set it to something different if required.
    DetermineDate.Year = Year(dtDate)

    Select Case Month(dtDate)
        Case 1, 2, 3
            DetermineDate.Quarter = 4
            DetermineDate.EndDate = DateSerial(DetermineDate.Year, 12, 31)
            DetermineDate.Year = DetermineDate.Year - 1

        Case 4, 5, 6
            DetermineDate.Quarter = 1
            DetermineDate.EndDate = DateSerial(DetermineDate.Year, 3, 31)

        Case 7, 8, 9
            DetermineDate.Quarter = 2
            DetermineDate.EndDate = DateSerial(DetermineDate.Year, 6, 30)

        Case 10, 11, 12
            DetermineDate.Quarter = 3
            DetermineDate.EndDate = DateSerial(DetermineDate.Year, 9, 30)

        Case Else
            ' For this scenario, you don't need an else but it's here anyway.
            Exit Function

    End Select
End Function

Public Sub CallDetermineDate()
    Dim objResult As clsObject

    Set objResult = DetermineDate(Now)

    Debug.Print "End Date = " & objResult.EndDate
    Debug.Print "Year = " & objResult.Year
    Debug.Print "End Month = " & objResult.EndMonth
    Debug.Print "Quarter = " & objResult.Quarter
    Debug.Print "Quarter Text = " & objResult.QuarterText
    Debug.Print "Date String = " & objResult.DateString
End Sub

...现在运行CallDetermineDate例程,您将看到输出。

无论我是否正确设置了逻辑,它都试图说明VBA中不同对象的用法以及它们如何挂在一起。

顺便说一句-函数可以返回值,而子函数则不能。

在VBA中通常有多种处理方法,有些方法比其他方法要好,但是很多时候,这取决于方案和解决方案需要走多远的距离,即边缘情况,数据集,性能与可读性等。

上述方法也可以通过几种不同的方式来完成,特别是确定日期,所有这些代码实际上都可以构成类模块本身的构造函数(而不是在模块世界中闲逛)。再次,有很多做事的方法。

希望它能自我解释,如果没有的话,请问一下,但我希望能有所帮助。