当第一个参数为false时,VBA“And”运算符是否会计算第二个参数?

时间:2011-08-10 17:53:58

标签: excel vba operators

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为false,但是仍在评估之后的语句。这给了我运行时超出范围错误的下标。

VBA是否真的继续评估And的第二个参数,即使第一个参数是假的?

7 个答案:

答案 0 :(得分:54)

您正在寻找的是“短路评估”。

VBA没有它。

您可以看到一种可能适合您的情况的方法here

在那里选择的方法涉及用Select Case代替If。还有一个使用嵌套Ifs的例子。

答案 1 :(得分:10)

作为DOK mentioned:不,VBA没有短路评估。

使用2 If-then语句而不是使用AND运算符在技术上更有效率,但除非你这么做很多次,否则你不会注意到节省,所以不管怎么说更具可读性。如果你想获得真正的技术,VBA也会比If-then语句更快地处理多个Select Case语句。

VBA古怪:)

答案 2 :(得分:2)

答案是肯定的,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

答案 3 :(得分:2)

VBA确实有一种短路行为。 通常Null通过表达式传播,例如。 3 + NullNullTrue And NullNull。 但是:

  

? False And Null
  False

这看起来像是短路行为 - 发生了什么?当结点(Null)的另一个参数为AndFalse时,0不会传播 - 结果只是False或{{ 1}}。如果它是左或右参数并不重要。如果析取(0)的另一个参数是Or或非零整数(浮点值将使用this rule舍入为整数),则同样适用。

因此,在TrueAnd的参数中无法阻止副作用和错误,但Or传播可能会被短路&#34; 。此行为似乎继承自SQL

答案 4 :(得分:1)

由于答案是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

解决方案

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级联的一行上,并且有相当多的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

答案 5 :(得分:0)

我认为这是最好的做法:

function so_39267627_form_field( $field, $key, $args, $value ){

if ( $args['required'] ) {
    $args['class'][] = 'validate-required';
    $required = ' <abbr class="required" title="' . esc_attr__( 'required', 'woocommerce'  ) . '">*</abbr>';
} else {
    $required = '';
}

$args['maxlength'] = ( $args['maxlength'] ) ? 'maxlength="' . absint( $args['maxlength'] ) . '"' : '';



if ( is_string( $args['label_class'] ) ) {
    $args['label_class'] = array( $args['label_class'] );
}

if ( is_null( $value ) ) {
    $value = $args['default'];
}

// Custom attribute handling
$custom_attributes = array();

// Custom attribute handling
$custom_attributes = array();

if ( ! empty( $args['custom_attributes'] ) && is_array( $args['custom_attributes'] ) ) {
    foreach ( $args['custom_attributes'] as $attribute => $attribute_value ) {
        $custom_attributes[] = esc_attr( $attribute ) . '="' . esc_attr( $attribute_value ) . '"';
    }
}

$field = '';
$label_id = $args['id'];
$field_container = '<p class="form-row %1$s" id="%2$s">%3$s</p>';

$field .= '<input type="' . esc_attr( $args['type'] ) . '" class="input-text ' . esc_attr( implode( ' ', $args['input_class'] ) ) .'" name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" placeholder="' . esc_attr( $args['placeholder'] ) . '" ' . $args['maxlength'] . ' ' . $args['autocomplete'] . ' value="' . esc_attr( $value ) . '" ' . implode( ' ', $custom_attributes ) . ' required />';

if ( ! empty( $field ) ) {
    $field_html = '';

    $field_html .= $field;

    if ( $args['description'] ) {
        $field_html .= '<span class="description">' . esc_html( $args['description'] ) . '</span>';
    }

    if ( $args['label'] && 'checkbox' != $args['type'] ) {
        $field_html .= '<label for="' . esc_attr( $label_id ) . '" class="' . esc_attr( implode( ' ', $args['label_class'] ) ) .'">' . $args['label'] . $required . '</label>';
    }

    $container_class = 'form-row ' . esc_attr( implode( ' ', $args['class'] ) );
    $container_id = esc_attr( $args['id'] ) . '_field';

    $after = ! empty( $args['clear'] ) ? '<div class="clear"></div>' : '';

    $field = sprintf( $field_container, $container_class, $container_id, $field_html ) . $after;
}
return $field; }

add_filter( 'woocommerce_form_field_password', 'so_39267627_form_field', 10, 4 );
add_filter( 'woocommerce_form_field_text', 'so_39267627_form_field', 10, 4 );
add_filter( 'woocommerce_form_field_email', 'so_39267627_form_field', 10, 4 );
add_filter( 'woocommerce_form_field_tel', 'so_39267627_form_field', 10, 4 );
add_filter( 'woocommerce_form_field_number', 'so_39267627_form_field', 10, 4 );

因此,当且仅当条件i已满时,您才会通过条件。

答案 6 :(得分:-1)

考虑必须运行的机器代码。 最快的应该是像......的混合代码一样。

如果sfsf然后转到SkipAB

如果fdf然后goto goneBad

如果dffdefedwf然后转到MustHave

SkipAB:  如果dsda&gt; 4然后是MustHave

GoneBad: 退出功能

MustHave: ThisIS = true

&#39;程序必须运行它只会节省一些时间 成千上万次...例如,搜索大型驱动器的文件 或者,当使用简单的布尔测试来跳过耗时的函数时 比如在封闭的工作表中查找所有工作表和名称 [代码]

     If Not wFF.UsingFileExtMatch Then GoTo SkipExt
                If Not wFF.OKFileEXTMatch Then GoTo BADFile

SkipExt:                     如果不是wFF.UsingFileNameMatch那么GoTo SkipFileMatch                     如果不是wFF.OKFileNameMatch那么GoTo BADFile SkipFileMatch:                     如果不是wFF.UsingDaysAgo那么GoTo SkipDaysAgo                     如果不是wFF.OKDaysAgo那么GoTo BADFile SkipDaysAgo:

[/代码]