TclOO:将变量声明为“类”级别或在构造函数中的区别

TclOO:将变量声明为“类”级别或在构造函数中的区别,tcl,Tcl,我对在哪里使用变量myVar以及何时需要使用变量myVar感到困惑 如果我这样定义一个类,则不会出现错误: oo::class create Foo { variable bar constructor {input} { set bar $input } method twoBar {} { return [expr {2 * $bar}] } } 然后 但如果我将变量条移动到构造函数中: oo::class cre

我对在哪里使用变量myVar以及何时需要使用变量myVar感到困惑

如果我这样定义一个类,则不会出现错误:

oo::class create Foo {
    variable bar

    constructor {input} {
        set bar $input
    }

    method twoBar {} {
        return [expr {2 * $bar}]
    }
}
然后

但如果我将变量条移动到构造函数中:

oo::class create Foo {
    constructor {input} {
        variable bar
        set bar $input
    }

    method twoBar {} {
        return [expr {2 * $bar}]
    }
}
然后

通过将我的变量bar添加到twoBar方法中,可以解决这个问题

发生了什么?

在该方法中,variable只是您知道的常规Tcl命令,而当前名称空间是每个对象拥有的实例名称空间

在声明级别,变量是不同的。这是一个完全不同的命令。它为配置的实体(示例中的类Foo)配置变量解析器,使其具有一个变量名,如果在本地方法作用域中找不到该变量名,则将在实例命名空间中查找该变量名;解析器实际上会对变量名列表执行此操作。变量解析器适用于该类定义的所有方法,但不适用于它的子类或超类,我用另一种方法尝试过;如果是在实例定义上下文中,那么它就是一个实例

在您的示例中,这意味着:

在第一种情况下,构造函数和twoBar方法都让bar实际上引用变量::oo::Obj12::bar,这是在TclOO实现中关于名称空间名称的通常假设下实现的。 在第二种情况下,虽然从技术上讲,bar在构造函数中引用::oo::Obj12::bar,但仅在调用变量之后,在twoBar方法中,bar仅引用从未设置过的局部变量。这意味着,由于变量读取的标准语义,从bar读取失败。 调用我的变量栏与该方法中的变量栏具有相同的效果。区别在于,一个可以升级为可从对象外部调用,另一个可以同时绑定和设置多个变量;两者都是不常见的用途,但本质上是不同的

在字节码级别,两种twoBar方法之间唯一明显的区别是在第一个版本中:

% tcl::unsupported::disassemble method Foo twoBar
ByteCode 0x0x7ff5a2845810, refCt 1, epoch 17, interp 0x0x7ff5a2808010 (epoch 17)
  Source "\n        return [expr {2 * $bar}]\n  ..."
  Cmds 2, src 38, inst 6, litObjs 1, aux 0, stkDepth 2, code/src 0.00
  Proc 0x0x7ff5a2835f10, refCt 1, args 0, compiled locals 1
      slot 0, scalar, resolved, "bar"
  Commands 2:
      1: pc 0-5, src 9-32        2: pc 0-4, src 17-31
  Command 1: "return [expr {2 * $bar}]..."
  Command 2: "expr {2 * $bar}..."
    (0) push1 0     # "2"
    (2) loadScalar1 %v0     # var "bar"
    (4) mult 
    (5) done 
插槽0中的变量条标记为“已解决”,它在封面下进行恶作剧。它们在其他方面完全相同。构造函数在命令序列上有很大的差异,所以比较起来很困难,但是我们仍然可以看到那里发生了什么

% tcl::unsupported::disassemble constructor Foo
ByteCode 0x0x7ff5a2845c10, refCt 1, epoch 17, interp 0x0x7ff5a2808010 (epoch 17)
  Source "\n        set bar $input\n  ..."
  Cmds 1, src 28, inst 5, litObjs 0, aux 0, stkDepth 1, code/src 0.00
  Proc 0x0x7ff5a2835d90, refCt 1, args 1, compiled locals 2
      slot 0, scalar, arg, "input"
      slot 1, scalar, resolved, "bar"
  Commands 1:
      1: pc 0-3, src 9-22
  Command 1: "set bar $input..."
    (0) loadScalar1 %v0     # var "input"
    (2) storeScalar1 %v1    # var "bar"
    (4) done 

再次,bar被解析…

你会认为第一种情况是惯用的风格吗?
% tcl::unsupported::disassemble method Foo twoBar
ByteCode 0x0x7ff5a2845810, refCt 1, epoch 17, interp 0x0x7ff5a2808010 (epoch 17)
  Source "\n        return [expr {2 * $bar}]\n  ..."
  Cmds 2, src 38, inst 6, litObjs 1, aux 0, stkDepth 2, code/src 0.00
  Proc 0x0x7ff5a2835f10, refCt 1, args 0, compiled locals 1
      slot 0, scalar, resolved, "bar"
  Commands 2:
      1: pc 0-5, src 9-32        2: pc 0-4, src 17-31
  Command 1: "return [expr {2 * $bar}]..."
  Command 2: "expr {2 * $bar}..."
    (0) push1 0     # "2"
    (2) loadScalar1 %v0     # var "bar"
    (4) mult 
    (5) done 
% tcl::unsupported::disassemble constructor Foo
ByteCode 0x0x7ff5a2845c10, refCt 1, epoch 17, interp 0x0x7ff5a2808010 (epoch 17)
  Source "\n        set bar $input\n  ..."
  Cmds 1, src 28, inst 5, litObjs 0, aux 0, stkDepth 1, code/src 0.00
  Proc 0x0x7ff5a2835d90, refCt 1, args 1, compiled locals 2
      slot 0, scalar, arg, "input"
      slot 1, scalar, resolved, "bar"
  Commands 1:
      1: pc 0-3, src 9-22
  Command 1: "set bar $input..."
    (0) loadScalar1 %v0     # var "input"
    (2) storeScalar1 %v1    # var "bar"
    (4) done