2

Upvar 在不同的堆栈帧(有时称为调用堆栈不同的范围)中创建指向变量的链接。

Upvar 还用于为全局(或命名空间)变量2创建别名。但是命名空间只能由namespace eval命令创建。proc 命令创建一个新的堆栈帧。

命名空间和调用堆栈似乎是TCL 命名上下文可以更改的两种方式。Upvar 和 Uplevel 可以在命名空间和调用堆栈上工作。

我做对了吗?我还没有看到调用堆栈和命名空间之间的直接比较,因此我提出了问题。

4

1 回答 1

8

不,不完全是。命名空间和调用框架是非常不同的概念。命名空间是名称的层次结构,可以消除同义词的歧义。您可能foo在程序中命名了三个变量,但如果您将它们放在不同的命名空间中,它们不会发生冲突。命名空间可用于变量名和命令名。使用命名空间的内容创建后,namespace eval在您调用它之前始终可以访问namespace delete它。

调用堆栈是一系列堆栈帧。第一个堆栈帧 #0 始终存在。每当调用命令时都会创建其他堆栈帧(这主要用于作为用户定义过程的命令,“内置”命令遵循它们自己的规则)。当命令返回时,它们再次被销毁。因此,如果您调用命令 A,A 调用命令 B,B 调用命令 C,则调用堆栈如下所示:

#3 : <C's variables>
#2 : <B's variables>
#1 : <A's variables>
#0 : <global and namespace variables>

每个堆栈帧都是一个范围,因为只有在那里创建或导入其中的变量才能被访问,除非您使用upvar. 其他一切都是隐藏的。在大多数编程语言中,可以从内部范围自动访问外部范围(例如全局范围)的名称。在 Tcl 中并非如此。

使用upvar你可以让命令查看它自己的堆栈框架之外的东西。例如,C 可以使用为全局变量upvar #0 foo bar创建别名 ( ) ,或使用(注意不带 #)为B 的堆栈帧中的变量创建别名 ( )。barfooupvar 1 baz quxquxbaz

uplevel命令可以按照相同的方式在另一个堆栈帧中执行脚本,包括全局堆栈帧。在执行期间,脚本可以访问该堆栈帧中的所有内容,但不能访问其他内容,包括uplevel从中调用的堆栈帧中的变量。

::abc::defC 也可以使用为命名空间变量创建别名upvar #0 ::abc::def ghi,但不要这样做,namespace upvar ::abc def ghi而是使用。

而不是upvar #0 foo foo你可以使用global foo来导入一个全局变量。在命名空间中定义的命令内部,该variable命令可以导入在同一命名空间中定义的变量。

它通常对#0(全局框架)或1(调用者的框架)upvar很有用。uplevel使用其他帧号很容易出错,通常表示设计不佳。该调用为同一堆栈帧中的变量 ( )upvar 0 foo bar创建别名 ( ) ,这可能非常有用。barfoo

由正在处理的事件调用的命令在调用堆栈之外使用全局级别执行。他们无法进入活动堆栈框架并访问驻留在那里的变量。

一个简单的演示:

namespace eval ::abc {
    variable def 42

    proc xyz {} {
        variable def
    }
}

set foo 1138

proc A {} {
    B
}

proc B {} {
    set baz 1337
    C
}

proc C {} {
    upvar #0 foo bar
    puts $bar
    upvar 1 baz qux
    puts $qux
    namespace upvar ::abc def ghi
    puts $ghi
}
于 2015-01-15T19:04:54.550 回答