我试图在tclOO
中将变量的引用作为数据成员。是否有任何构造可以模拟函数内部使用的upvar
命令。
oo::class create TestClass {
variable refToVar
constructor {varRef} {
set varRef [string range $varRef 1 [expr [string length $varRef] -1]]
# upvar $varRef temp
# set refToVar $temp
upvar $varRef refToVar
}
method getRefToVar {} {
return $refToVar
}
method setRefToVar {value} {
set refToVar $value
}
}
proc testProc {varRef} {
set varRef [string range $varRef 1 [expr [string length $varRef] -1]]
upvar $varRef temp
set temp 5
}
puts "start"
set x 100
puts $x
puts "proc test"
testProc {$x}
puts $x
puts "tclOO test"
TestClass create tst {$x}
puts [tst getRefToVar]
tst setRefToVar 20
puts [tst getRefToVar]
puts $x
基本上我希望与oo::class
使用proc
实现相同的行为。
答案 0 :(得分:1)
等效于upvar
。 但是你需要在正确的上下文中运行upvar
,即对象的状态命名空间而不是方法(因为你希望refToVar
是整个对象),因为upvar
总是在当前堆栈上下文中引用它,这意味着默认情况下两个中的局部变量。
TclOO提供了这些工具。
my eval [list upvar 2 $varref refToVar]
这是2
,因为my eval
有一个级别,方法有另一个级别。 确定 你只引用命名空间中的变量(全局变量在全局命名空间中)这样或者你会很痛苦。在对象内部保留对变量的引用可能不是一个好主意,因为它们可以从各种上下文中运行,但它应该可以工作。
您还可以使用生成namespace upvar
内部的my eval
命令来执行相同的操作,因为可以使用命名空间/ varname坐标而不是堆栈来描述您可以编写的任何合理的内容。 index / varname coordinates。
[编辑]:事实上,Tcl注意避免恶案,如果你尝试就会抛出错误。这是该课程的简化版本:
oo::class create Foo {
constructor v {
# This is done like this because this sort of operation is not affected
# by the 'variable' declaration; it's a bit unusual...
my eval [list upvar 2 $v ref]
}
variable ref
method bar {} {
# Just some normal usage
puts $ref
}
}
让我们看一下这个正常工作:
% set x 123
123
% set y [Foo new x]
::oo::Obj12
% $y bar
123
% incr x
124
% $y bar
124
好的,一切看起来都不错。现在,让我们将它附加到局部变量:
proc abc {} {
set x 234
set y [Foo new x]
# Try the call
$y bar
incr x
$y bar
# Try returning it to see what happens with the lifespan
return $y
}
但是调用它并不成功:
% abc
bad variable name "ref": can't create namespace variable that refers to procedure variable
% puts $errorInfo
bad variable name "ref": can't create namespace variable that refers to procedure variable
while executing
"upvar 2 x ref"
(in "my eval" script line 1)
invoked from within
"my eval [list upvar 2 $v ref]"
(class "::Foo" constructor line 1)
invoked from within
"Foo new x"
(procedure "abc" line 3)
invoked from within
"abc"
所以你去;您可以轻松地将外部全局(或命名空间)变量轻松链接到对象中,但最好不要将其作为局部变量。它可能可以做一些额外的工作来使用一个实际上是全局变量工作的upvar
的局部变量,但你为什么要打扰呢?
答案 1 :(得分:0)
Donal已经回答了你的问题,但还有更好的方法。
oo::class create TestClass {
variable varRef
constructor args {
lassign $args varRef
}
method getRefToVar {} {
upvar 0 $varRef refToVar
return $refToVar
}
method setRefToVar {value} {
upvar 0 $varRef refToVar
set refToVar $value
}
}
在这里,您为TestClass
对象提供变量的限定名称,例如全局变量::x
的{{1}}或x
的{{1}} ::foobar::x
命名空间中的变量。您可以在实例变量x
中保存此名称。
当您想要读取或写入此变量时,首先要创建一个本地引用:
foobar
这意味着在您调用此命令的方法中,varRef
是名称存储在upvar 0 $varRef refToVar
中的变量的引用或别名。
您可以在命令过程中执行相同的操作:
refToVar
测试它:
varRef
请注意名称的编写方式不同。
如果您以某种方式不知道变量的限定名称,这应该会有所帮助:
proc testProc {varRef} {
upvar 0 $varRef refToVar
set refToVar 5
}
BTW:你不必写
puts "start"
set x 100
puts $x
puts "proc test"
testProc ::x
puts $x
puts "tclOO test"
TestClass create tst ::x
puts [tst getRefToVar]
tst setRefToVar 20
puts [tst getRefToVar]
puts $x
有一个缩写形式:
TestClass create tst [namespace which -variable x]
但你也不必写:要从字符串的左端删除零个或多个美元符号,只需
string range $varRef 1 [expr [string length $varRef] -1]
但您也不必写:在传递变量名时不需要添加美元符号:
string range $varRef 1 end