VBA: когда использовать функцию против размещения кода в основной подпрограмме - PullRequest
1 голос
/ 27 апреля 2019

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

Первое, что должен сделать мой код, - это получить три переменные, связанные с датой / датой:

  • Текущий год или, в случае данных за 4 квартал, год до этого
  • В зависимости от квартала, переменная, которая выглядит следующим образом: 12.31.2018 для Q4 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

Однако мой вопрос: есть ли причина не помещать это в основной саб?У меня было такое представление, что было бы хорошо сосредоточиться на сути кода (скопировать все данные) в основной подпрограмме.Поэтому я чувствовал, что имеет смысл поместить код для определения даты в отдельную функцию.Тем не менее, я обнаружил, что возвращать несколько значений не просто и не является целью функции.Кроме того, я бы назвал эту функцию только один раз, поэтому, подумав, я пришел к выводу, что, вероятно, не имеет смысла помещать это куда-то еще.В основном я спрашиваю, что является хорошей практикой при использовании функций и нескольких подпрограмм.

1 Ответ

2 голосов
/ 27 апреля 2019

Вы можете сделать что-то вроде этого.

Добавьте приведенный ниже код в новый МОДУЛЬ КЛАССА в редакторе VBA и обязательно дайте этому новому классу имя 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 ...

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

Вышеприведенное также может быть выполнено несколькими различными способами, особенно DetermineDate, весь этот код может фактически формировать конструктор (что-то вроде) для самого модуля класса, а не зависать в мире модулей. Опять же, есть куча способов сделать что-то.

Надеюсь, это как-то само собой объясняется, если нет, спросите, но я надеюсь, что это поможет, хотя бы немного.

...