如何在tclOO中声明数据成员引用

时间:2016-06-17 13:55:42

标签: oop tcl

我试图在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实现相同的行为。

2 个答案:

答案 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

文档:exprincrlassignnamespaceprocputsset,{{3 },string