VBA:何时使用函数与将代码放在主子中
VBA: When to use a function vs putting code in the main sub
我正在尝试将来自不同文件的季度数据编译到单个 Excel 工作簿中的过程自动化(这需要每个季度完成)。现在我到了一个地步,我想知道我是否应该将所有代码保留在主子中,或者我是否应该使用一个函数。我真的很想听听与 VBA 合作超过几个月并学习如何编写高效且可读代码的人的意见。
我的代码需要做的第一件事是获取三个 date/date 相关变量:
- 当前年份,如果是第四季度数据,则为前一年
- 根据季度,变量如下所示:2018 年第四季度为 12.31.2018;对于 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
但是,我的问题是:有什么理由不把它放在主子中吗?我有这样的想法,即专注于主子中的代码(复制粘贴所有数据)会很好。所以我觉得把确定日期的代码放在一个单独的函数中是有意义的。但是,我发现返回多个值并不简单,也不是函数的意图。另外,我只会调用这个函数一次,所以经过一番思考后我得出结论,将它放在其他地方可能没有意义。基本上我问的是使用函数和多个子项时的良好做法是什么。
你可以这样做。
将以下代码添加到 VBA 编辑器中的新 CLASS MODULE 并确保为新的 class 命名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 中不同对象的用法以及它们如何组合在一起。
顺便说一句 - 一个函数可以 return 一个值,sub 不能。
在 VBA 中通常有多种方法可以做事,有些方法比其他方法更好,但很多时候,这取决于场景以及您需要采用多远的解决方案,即边缘情况、数据集的大小、性能与可读性等
上面的内容也可以通过几种不同的方式完成,尤其是 DetermineDate,所有这些代码实际上可以为 class 模块本身形成一个构造函数(很好),而不是在模块中闲逛世界。同样,有很多方法可以做事。
希望它能自己解释,如果没有,请离开,但我希望能有所帮助,即使只是一点点。
我正在尝试将来自不同文件的季度数据编译到单个 Excel 工作簿中的过程自动化(这需要每个季度完成)。现在我到了一个地步,我想知道我是否应该将所有代码保留在主子中,或者我是否应该使用一个函数。我真的很想听听与 VBA 合作超过几个月并学习如何编写高效且可读代码的人的意见。
我的代码需要做的第一件事是获取三个 date/date 相关变量:
- 当前年份,如果是第四季度数据,则为前一年
- 根据季度,变量如下所示:2018 年第四季度为 12.31.2018;对于 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
但是,我的问题是:有什么理由不把它放在主子中吗?我有这样的想法,即专注于主子中的代码(复制粘贴所有数据)会很好。所以我觉得把确定日期的代码放在一个单独的函数中是有意义的。但是,我发现返回多个值并不简单,也不是函数的意图。另外,我只会调用这个函数一次,所以经过一番思考后我得出结论,将它放在其他地方可能没有意义。基本上我问的是使用函数和多个子项时的良好做法是什么。
你可以这样做。
将以下代码添加到 VBA 编辑器中的新 CLASS MODULE 并确保为新的 class 命名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 中不同对象的用法以及它们如何组合在一起。
顺便说一句 - 一个函数可以 return 一个值,sub 不能。
在 VBA 中通常有多种方法可以做事,有些方法比其他方法更好,但很多时候,这取决于场景以及您需要采用多远的解决方案,即边缘情况、数据集的大小、性能与可读性等
上面的内容也可以通过几种不同的方式完成,尤其是 DetermineDate,所有这些代码实际上可以为 class 模块本身形成一个构造函数(很好),而不是在模块中闲逛世界。同样,有很多方法可以做事。
希望它能自己解释,如果没有,请离开,但我希望能有所帮助,即使只是一点点。