Upvar 在不同的堆栈帧(有时称为调用堆栈或 不同的范围)中创建指向变量的链接。
Upvar 还用于为全局(或命名空间)变量2创建别名。但是命名空间只能由namespace eval命令创建。proc 命令创建一个新的堆栈帧。
命名空间和调用堆栈似乎是TCL 命名上下文可以更改的两种方式。Upvar 和 Uplevel 可以在命名空间和调用堆栈上工作。
我做对了吗?我还没有看到调用堆栈和命名空间之间的直接比较,因此我提出了问题。
Upvar 在不同的堆栈帧(有时称为调用堆栈或 不同的范围)中创建指向变量的链接。
Upvar 还用于为全局(或命名空间)变量2创建别名。但是命名空间只能由namespace eval命令创建。proc 命令创建一个新的堆栈帧。
命名空间和调用堆栈似乎是TCL 命名上下文可以更改的两种方式。Upvar 和 Uplevel 可以在命名空间和调用堆栈上工作。
我做对了吗?我还没有看到调用堆栈和命名空间之间的直接比较,因此我提出了问题。
不,不完全是。命名空间和调用框架是非常不同的概念。命名空间是名称的层次结构,可以消除同义词的歧义。您可能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 的堆栈帧中的变量创建别名 ( )。bar
foo
upvar 1 baz qux
qux
baz
该uplevel
命令可以按照相同的方式在另一个堆栈帧中执行脚本,包括全局堆栈帧。在执行期间,脚本可以访问该堆栈帧中的所有内容,但不能访问其他内容,包括uplevel
从中调用的堆栈帧中的变量。
::abc::def
C 也可以使用为命名空间变量创建别名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
创建别名 ( ) ,这可能非常有用。bar
foo
由正在处理的事件调用的命令在调用堆栈之外使用全局级别执行。他们无法进入活动堆栈框架并访问驻留在那里的变量。
一个简单的演示:
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
}