【问题标题】:VBS logical operators initialise empty variables?VBS逻辑运算符初始化空变量?
【发布时间】:2015-09-19 17:24:49
【问题描述】:

考虑以下 VBS:

dim msg, myVar, myVar2

msg = "myVar = " & myVar
msg = msg & vbcrlf & "myVar = empty: " & isempty(myVar)
msg = msg & vbcrlf & "TypeName(myVar) = " & TypeName(myVar)

msgbox msg, , "BEFORE"

if not myVar then myVar2 = true

msg = "myVar = " & myVar
msg = msg & vbcrlf & "myVar = empty: " & isempty(myVar)
msg = msg & vbcrlf & "TypeName(myVar) = " & TypeName(myVar)

msgbox msg, , "AFTER"

我希望 "BEFORE" 和 "AFTER" 的输出是相同的......我们所做的只是与未初始化(空)的变体进行比较,对吧?

但是 - 似乎“如果不是”实际上将其初始化为(长)零! 我已经在 VBS (ASP) 中编码多年了,这对我来说是新的!

需要注意的几点:

  • .vbs 和等效 ASP 代码(在我的 Win 7 桌面和 Server 2008 R2 上)的行为相同。
  • 所有逻辑运算符 - and/or/not/xor 都会产生这种效果
  • 比较运算符没有。

对于粗心的人来说,这似乎是一个潜在的陷阱……谁能解释这种行为?

【问题讨论】:

    标签: vbscript


    【解决方案1】:

    我找不到有关此问题的任何官方信息。 在做了一些测试后,我认为这是一个错误或未记录的效果。 此行为不适用于 VBA 和 VB6 等其他类似平台。

    Visual Basic for Application:

    Visual Basic 6:

    VBScript:

    作为一种解决方法,按值传递表达式是可行的。

    If Not (exp) Then
    'or
    If Not CBool(exp) Then
    

    ByRef and ByVal Parameters
    CBool Function

    【讨论】:

    • 感谢您在 VB6 和 VBA 中进行测试。我同意这看起来像一个错误。在使用 VBA 编程了大约 20 年之后,这对我来说也是一个新的东西!
    【解决方案2】:

    如果你把中间那句话改成

    if not (myVar) then myVar2 = true   'with parenthesis around myVar
    

    那么您将不会看到相同的行为。 BEFORE 和 AFTER 现在是一样的。

    这是因为括号显然强制 Not 运算符只执行逻辑测试,并且会跳过 Not 的副作用。

    https://msdn.microsoft.com/en-us/subscriptions/9cy86sfb%28v=vs.84%29.aspx你会发现以下关于Not的信息

    In addition, the Not operator inverts the bit values of any variable and
    sets the corresponding bit in result according to the following table:
    +-------------------+---------------+
    | Bit in expression | Bit in result |
    +-------------------+---------------+
    | 0                 | 1             |
    | 1                 | 0             |
    +-------------------+---------------+
    

    例如

    Msgbox Not 2   ' is -3
    Msgbox Not -3  ' is 2
    

    如果您认为在内部将值存储为有符号字节/字,这是有道理的。

    000 -4
    001 -3  --> 001 inverted is 110
    010 -2
    011 -1
    100  0
    101  1
    110  2  --> 110 inverted is 001
    111  3
    

    让我们将 Empty 转换为 Long

    x = CLng(myVar)
    

    你会发现x的值为0。

    如果你使用

    if not myVar then myVar2 = true
    

    然后将计算 not myVar 的结果(结果值 -1 将随后被丢弃)。但无论如何都会进行计算,为此必须先将 Empty 转换为 long。

    【讨论】:

    • not myVarnot (myVar) 之间的区别很有趣,谢谢。但是,您链接的文章在赋值语句中讨论了结果的值 - 所以我仍在寻找关于非赋值语句(if)如何赋值的答案!
    • if 不会分配值,not 会。您可以用result = Not myVar 或简单地MsgBox Not myVar 替换您的语句,仍然会看到相同的行为。那是因为需要评估not myVar。在翻转位之前,Empty 必须首先转换为其 Long 表示。显然,在执行语句后它保持长。这可能被认为是一个错误。
    • 如果您阅读了Eric Lippert's article 关于括号的使用,那么为什么使用MsgBox Not (myVar) 而不是MsgBox Not myVar 不会导致BEFORE/AFTER 发生变化也是有道理的。使用括号会导致参数按值传递。这意味着 Not 运算符适用于myVar副本,因此原始myVar 保持不变。
    • 作为第一个在这里使用“bug”这个词的人做得很好,这就是我相信的!或者至少,一个未记录的 feature :)。我确实考虑过括号的含义,但由于该语句不是函数调用,我不确定这是否适用?
    • @beercohol 我同意你的看法。错误或未记录的行为。看看我的记录 Visual Basic for Application : i.imgur.com/exNuoCK.gif Visual Basic 6 : i.imgur.com/vtNqm1t.gif 最后是越野车 VBScript : i.imgur.com/NbbZy5P.gif BTW myVar(myVar) 之间的区别并不有趣,请参阅另一个文档(第 4 段):msdn.microsoft.com/en-us/library/ee478101%28v=vs.84%29.aspx (... T如果参数包含在括号中,则按值传递 ...)。另一种选择是使用 CBool​​(例如If Not CBool(myVar) Then ...
    【解决方案3】:

    https://technet.microsoft.com/en-us/library/ee198865.aspx

    因此,如果您创建一个变量而不对其进行初始化,该变量将采用以下默认值之一: 如果将变量用作字符串,则初始值为 Empty。 如果将变量用作数字,则初始值为 0。

    我认为,由于您正在进行布尔检查,因此您实际上是在使用 myVar 作为数字,并且您的语句读作如下:

    if not 0 then myVar2 = true
    'same as: if not FALSE then myVar2 = true
    

    所以 myVar 被初始化为 0

    【讨论】:

    • 感谢有趣的链接,但它只讨论了变量在使用它们的表达式中被强制为空字符串或零的默认值,没有关于值的实际 assignment .在该页面上的 X 和 Y 示例中,变量始终如预期的那样保持为空。
    • @beercohol - 实际上它确实谈到了赋值(在页面中间):“尚未初始化的数值变量会自动赋值值0"
    • 确实是这么说的,但很明显,它实际上意味着在 表达式 的上下文中“分配”,其中 使用了空变量。我认为“已分配”一词的使用非常宽松,它并没有说明在非赋值语句中将任何值存储到变量中。
    【解决方案4】:

    这些是来自 VBA https://msdn.microsoft.com/en-us/library/ee177324.aspx?f=255&MSPPError=-2147217396 的规则

    关键是变量(尽管不是对象)总是有一个可用的值(对象确实没有任何值)。

    5.5.1.2.2 强制转换为布尔值

    不存储为布尔值时,False 用 0 表示,True 用非零值表示,通常为 -1。

    Boolean Let-coercion 的语义取决于源的值类型和目标的声明类型:

    Source Value Type Destination Declared Type Semantics
    Boolean     Boolean     The result is a copy of the source value.
    Boolean     Any numeric type except Byte     If the source value is False, the result is 0. Otherwise, the result is -1.
    Boolean     Byte     If the source value is False, the result is 0. Otherwise, the result is 255.
    Any numeric type     Boolean     If the source value is 0, the result is False. Otherwise, the result is True
    

    5.5.1.2.11 Let-coercion from Empty

    Empty Let-coercion 的语义取决于目标声明的类型:

    Source Value Type Destination Declared Type Semantics
    Empty Any numeric type The result is 0.
    Empty Boolean The result is False.
    Empty Date The result is 12/30/1899 00:00:00.
    Empty String The result is a 0-length string.
    Empty String * length The result is a string containing length spaces.
    Empty Any class or Object Runtime error 424 (Object required) is raised.
    Empty Any other type except Variant Runtime error 13 (Type mismatch) is raised.
    

    当您第一次使用消息框时,您的变量被强制转换为字符串。

    然后在上面一行中被强制为假。

    5.6.9.5 关系运算符 关系运算符是在其操作数之间执行比较的简单数据运算符。

    relational-operator = equality-operator / inequality-operator / less-than-operator / greaterthan-operator / less-than-equal-operator / greater-than-equal-operator
    

    静态语义:

    关系运算符静态解析为简单数据运算符。

    如果任何操作数的声明类型是数组或 UDT,则关系运算符无效。

    基于其操作数的声明类型,关系运算符具有以下声明类型:

    Left Operand Declared Type Right Operand Declared Type Operator Declared Type
    Any type except an array, UDT or Variant Any type except an array, UDT or Variant Boolean Any type except an array or UDT Variant 
     Variant Any type except an array or UDT Variant
    

    运行时语义

    关系运算符首先被评估为简单数据运算符。

    如果任何操作数的值类型是数组或 UDT,则会引发运行时错误 13(类型不匹配)。

    在计算关系运算符之前,它的非 Null 操作数经过 Let-coercion 到运算符的有效值类型。

    根据操作数的值类型确定有效值类型如下:

    5.6.9.5.1 = 运算符

    = 运算符对其操作数执行值相等比较。 等式运算符 = 表达式 "=" 表达式

    运行时语义:

    如果认为操作数相等,则返回 True。否则,返回 False。

    【讨论】:

    • 谢谢,但我没有在 VBA 中尝试过。另外,我在提供的链接中看不到您上面引用的任何内容。无论如何,我不相信 msgbox 在这里有任何相关性,这只是我在 VBS 中显示输出的方式。我猜你是说你相信我的变量被强制连接到“msg”变量中以进行输出。实际上,我确信在评估该 expression 时它会被强制(默认为空字符串),但这与进行永久分配不同。如果是这样,第一个“isempty”也会显示为 false。
    • 不,我是说它是被强制的,也许是在表达式评估中。对于 msgbox,它没有改变,因为它是一个函数 - 也许。
    • 我认为不可能,否则我对“isempty”和“typename”的第一次调用将返回非空值。这种变化似乎发生在“if not myVar then myVar2 = true”语句上。
    • Isempty 和 typename 是函数调用。
    猜你喜欢
    • 1970-01-01
    • 2015-04-09
    • 1970-01-01
    • 2017-03-19
    • 2012-07-14
    • 2014-04-13
    • 2017-05-31
    • 2020-11-26
    • 1970-01-01
    相关资源
    最近更新 更多