Оператор VBA "И" оценивает второй аргумент, когда первый является ложным?
Function Foo(thiscell As Range) As Boolean
Foo = thiscell.hasFormula And (InStr(1, UCase(Split(thiscell.formula, Chr(40))(0)), "bar") > 0)
End Function
Эта функция существует для проверки наличия определенной подстроки (bar в этом случае) до (.
Случай, с которым у меня возникают проблемы, - это когда ячейка, переданная в функцию, пуста, thisCell.hasFormula является ложной, но оператор после и все еще оценивается. Это дает мне индекс ошибок вне диапазона во время выполнения.
Действительно ли VBA продолжает оценивать второй аргумент для И, даже если первый был ложным?
Ответы
Ответ 1
То, что вы ищете, называется " оценка короткого замыкания".
У VBA этого нет.
Вы можете увидеть подход, который, вероятно, адаптируется к вашей ситуации здесь.
Подход, выбранный там, заключался в замене a Select Case
на If
. Существует также пример использования вложенных Ifs
.
Ответ 2
Как DOK, упомянутый: Нет, VBA не имеет оценки короткого замыкания.
Технически более эффективно использовать операторы 2 If-then
вместо использования оператора AND
, но если вы этого не делаете много раз, вы не заметите сбережений, поэтому идите на то, что более читаемо. И если вы хотите получить действительно технический, VBA обрабатывает несколько операторов If-then
быстрее, чем Select Case
.
VBA причудлива:)
Ответ 3
Ответ: да, VBA не проводит оценку короткого замыкания.
Это не просто вопрос стиля; это имеет большое значение в такой ситуации:
If i <= UBound(Arr, 1) And j <= UBound(Arr, 2) And Arr(i, 1) <= UBound(Arr2, 1) Then
Arr2(Arr(i, 1), j) = Arr(i, j)
End If
... что неверно. Более целесообразно:
If i <= UBound(Arr, 1) And j <= UBound(Arr, 2) Then
If Arr(i, 1) <= UBound(Arr2, 1) Then
Arr2(Arr(i, 1), j) = Arr(i, j)
End If
End If
Или, если у вас есть отвращение к вложенным ifs:
If i > UBound(Arr, 1) Or j > UBound(Arr, 2) Then
' Do Nothing
ElseIf Arr(i, 1) > UBound(Arr2, 1) Then
' Do Nothing
Else
Arr2(Arr(i, 1), j) = Arr(i, j)
End If
Ответ 4
У VBA есть одно поведение, подобное короткому замыканию.
Обычно Null
распространяется через выражения, например. 3 + Null
- Null
, а True And Null
- Null
.
Однако:
? False And Null
False
Это похоже на поведение короткого замыкания - что происходит? Null
не распространяется, когда другой аргумент конъюнкции (And
) равен False
или 0
- результат равен всего False
или 0
. Неважно, если это левый или правый аргумент. То же самое относится, если другим аргументом для дизъюнкции (Or
) является True
или ненулевое целое число (значение с плавающей запятой округляется до целого с помощью это правило).
Таким образом, побочные эффекты и ошибки не могут быть предотвращены в аргументах And
и Or
, но Null
распространение может быть "закорочено". Это поведение кажется унаследованным от SQL.
Ответ 5
Я думаю, что это лучшая практика:
sub my conditions()
If Condition1=constraint1 then
if Condition2=constraint2 then
if condition3=constraint3 then
...
....
end if
end if
end if
else
end if
....
end if
end sub
Таким образом, вы будете проходить только через условия тогда и только тогда, когда условие я заполнено.
Ответ 6
Так как ответ - один из лучших в Google, который ищет что-то вроде vba if condition not lazy
, я хотел бы привести более простой пример, проблему и решения обоих условий: AND
и более интересный OR
...
Dim cond1 As Boolean 'some 1st condition that may be True or False
Dim obj As Collection 'just some sample object that may or may not be instantiated
(²: мне лучше объяснить другим разработчикам, почему вы не выбрали OR
, если они не знают фона)
дело AND
cond1 = False
If cond1 Then Set obj = New Collection
проблема:
If cond1 And obj.Count > 0 Then Debug.Print "Count > 0!" 'throws error if < cond1 = False >
'because condition 2 is always evaluated
solution решение:
If cond1 Then If obj.Count > 0 Then Debug.Print "Count > 0!" 'AND would not short-cicuit!² https://stackoverflow.com/a/57521572/1915920
В зависимости от вкуса, сложности и читабельности может иметь смысл написать это так:
If cond1 Then
If obj.Count > 0 Then 'AND would not short-cicuit!² https://stackoverflow.com/a/57521572/1915920
Debug.Print "Count > 0!"
End If
End If
чехол OR
cond1 = True
If Not cond1 Then Set obj = New Collection 'obj stays < Nothing > otherwise
проблема:
If cond1 Or obj.Count = 0 Then Debug.Print "no objects!" 'throws error if < cond1 = True >
'because condition 2 is always evaluated
решение 1:
на месте, без резервирования, один вкладыш без GoTo
с использованием Select
:
Select Case True: Case cond1, obj.Count = 0: Debug.Print "no objects!": End Select 'OR would not short-cicuit!² https://stackoverflow.com/a/57521572/1915920
в случае, если он должен/должен быть на нескольких строках и с некоторыми другими:
Select Case True
Case cond1, obj.Count = 0 'OR would not short-cicuit!² https://stackoverflow.com/a/57521572/1915920
Debug.Print "no objects!"
Case Else
Debug.Print "object count: " & obj.Count
End Select
решение 2:
на месте, не избыточный код с минимальным использованием GoTo
, но более длинный If
многострочный код:
If cond1 Then
noObjs:
Debug.Print "no objects!"
ElseIf obj.Count = 0 Then 'OR would not short-cicuit!² https://stackoverflow.com/a/57521572/1915920
GoTo noObjs
End If
решение 3:
на месте, условия (могут подходить) в одной строке аналогично OR
-concatenation с довольно частым использованием GoTo
:
If cond1 Then GoTo noObjs ElseIf obj.Count = 0 Then GoTo noObjs 'OR would not short-cicuit!² https://stackoverflow.com/a/57521572/1915920
GoTo skipOnAllFalse
noObjs:
Debug.Print "no objects!"
skipOnAllFalse: 'use more specific label/scenario name if possible
решение 4:
код не на месте (Sub
), избегая GoTo
, условия (могут подходить) в одной строке, но код модуля/класса может быть более нечитаемым/распространяемым/загроможденным:
Private Sub noObjs(): Debug.Print "no objects!"
If cond1 Then noObjs ElseIf obj.Count = 0 Then noObjs 'OR would not short-cicuit!² https://stackoverflow.com/a/57521572/1915920
решение 5:
используя одну переменную условия:
Dim any As Boolean: any = cond1
If Not any Then any = obj.Count = 0 'OR would not short-cicuit!² https://stackoverflow.com/a/57521572/1915920
If any Then Debug.Print "no objects!"
решение 6:
используя несколько переменных условия:
Dim c1 As Boolean: Dim c2 As Boolean
c1 = cond1
If Not c1 Then c2 = obj.Count = 0 'OR would not short-cicuit!² https://stackoverflow.com/a/57521572/1915920
If c1 Or c2 Then Debug.Print "no objects!" 'safe to use Or now
Ответ 7
Рассмотрим машинный код, который должен быть запущен.
Самый быстрый должен быть по линии сочетания кода вроде...
если sfsf, а затем перейти SkipAB
если fdf затем перешел в
если dffdefedwf, а затем goto MustHave
SkipAB:
если dsda > 4, то MustHave
GoneBad:
функция выхода
Musthave:
ThisIS = true
'сохраняет только несколько моментов, когда программа должна запускать его
много тысяч раз... например, поиск файлов на большом диске
или когда простой булевский тест используется, чтобы пропустить функцию времени
как найти все листы и имена в закрытом листе
[Код]
If Not wFF.UsingFileExtMatch Then GoTo SkipExt
If Not wFF.OKFileEXTMatch Then GoTo BADFile
SkipExt: Если не wFF.UsingFileNameMatch, то GoTo SkipFileMatch Если не wFF.OKFileNameMatch Then GoTo BADFile
SkipFileMatch: Если не wFF.UsingDaysAgo, то GoTo SkipDaysAgo Если не wFF.OKDaysAgo, то GoTo BADFile
SkipDaysAgo:
[/код]