首页 > 解决方案 > upvar、TclOO 和 next - (可能)意外行为的解释

问题描述

我想知道是否有人可以解释为什么我可以在嵌套过程中成功链接 upvars 的幕后细节,但它在嵌套的 TclOO 方法中不起作用(这些方法在子类中被覆盖)。(有人告诉我,在 TclOO 类方法中调用 [next] 有点像“临时尾调用”,因为没有创建新的堆栈级别。是这样吗?如果是这样,完整的图片是什么?)

例如,以下三种方法并不都给出相同的结果:

proc addone {varname} {
  upvar $varname x;
  incr x;
}
proc addanotherone {varname} {
  upvar $varname xx;
  addone xx;
  incr xx;
}

oo::class create C1 {
  method addone {varname} {
    upvar $varname x;
    incr x;
  }
}
oo::class create S1 {
   superclass C1;
   method addone {varname} {
      upvar $varname xx;
      next xx;
      incr xx;
   }
 }
 oo::class create S2 {
   superclass C1;
   method addone {varname} {
     upvar $varname xx;
     next $varname;
     incr xx;
   }
 }
 set s1 [S1 new];
 set s2 [S2 new];
 set y 1;
 addanotherone y;
 set y; # First result gives 3, as expected;
 set y 1;
 $s1 addone y;
 set y; # gives 2, unexpected;
 set y 1;
 $s2 addone y; 
 set y;  #gives 3, unexpected, because original varname seems to be "two levels" deep.

如果 [next] 以某种方式在相同的堆栈级别运行,它可以在没有“uplevel”的情况下在调用者范围内创建变量吗?

如果不是,它不是真的在同一级别运行,那么它更像是一个闭包吗?

我对它与尾声、上层使用或应考虑的任何其他概念有何不同的真正细节感兴趣。谢谢!

标签: tcl

解决方案


next命令在内部有点像uplevel(特别是uplevel 1),因为它在运行超类实现时临时删除方法调用的堆栈帧,在返回时恢复堆栈帧(当然)。next next

这意味着您可以覆盖超类中的方法,而无需为这些超类特别准备。这是 Tcl 的一些其他旧对象系统的一个主要问题,您需要一个特殊的调用来获取upvarand的深度参数uplevel,而且很容易忘记这一点,所以我为 TclOO 更改了一些东西。但是,这种变化的直接后果意味着您正在做的事情S1 » addone将行不通。xx它在调用范围内创建/覆盖一个附加变量。S2 » addone是我认为惯用的。

如果你在一个方法和它覆盖的方法之间传递一个内部变量——根据定义,这需要两者合作——在对象的状态命名空间中使用一个变量;你的班级可以完全控制它。my或者通过or调用方法[self];这是一个标准的方法调用(所有暗示)。


推荐阅读