【问题标题】:TclOO Variable Scope with Inheritance/superclass具有继承/超类的 TclOO 变量范围
【发布时间】:2014-07-25 14:07:54
【问题描述】:

我在使用 TclOO 继承类时偶然发现了变量范围。 不重复声明,下面的成员变量nCrumbs对继承的类是不可见的。

有没有办法避免复制超类中的所有变量声明?

(我通读了所有 OO 文档,特别是 oo::define 和 oo::object,还有未导出的东西,用谷歌搜索它。有很多概念可以解决各种问题,我迷路了。 我正在寻找使继承类尽可能简单的东西。不过,超类中可能有任何花哨的复杂代码。)

任何帮助将不胜感激,谢谢。

oo::class create toaster {
    variable nCrumbs;                  #declaration

    constructor {} {
        set nCrumbs 0;                 #definition
    }

    method toast {nSlices} {
        if {$nCrumbs > 50} {
            error "== FIRE! FIRE! =="
        }
        set nCrumbs [expr $nCrumbs+4*$nSlices]
    }

    method clean {} {
        set nCrumbs 0
    }
}

oo::class create smartToaster {
    superclass toaster;                #inherit

    variable nCrumbs;                  #<======= have to declare again

    method toast {nSlices} {
        if {$nCrumbs > 40} {
            my clean
        }
        next $nSlices; #call superclass method
    }
}

set clsToaster [smartToaster new]
$clsToaster toast 2

【问题讨论】:

    标签: tcl scope superclass


    【解决方案1】:

    变量物理上位于(如果您可以对代码这么说的话)对象实例的私有命名空间中。通过在声明中使用variable,您可以指示方法绑定仅使其可用而无需进一步的命令。

    但是是的,子类也必须使用 variable 才能默认看到它,或者使用标准 Tcl 变量范围管理命令之一,例如 upvarnamespace upvar,甚至是私有的 @987654325 @方法

    oo::class create smartToaster {
        superclass toaster
        method toast {nSlices} {
            my variable nCrumbs
            if {$nCrumbs > 40} {
                my clean
            }
            next $nSlices
        }
    }
    
    oo::class create smartToaster {
        superclass toaster
        method toast {nSlices} {
            namespace upvar [namespace current] nCrumbs nc
            if {$nc > 40} {
                my clean
            }
            next $nSlices
        }
    }
    

    以前不是这样的,但后来发现它太混乱了;一个类的变量声明只影响该类的方法,而不影响它的子类。


    [编辑]:可以通过适当的元类魔法使父级的变量在子级中也可见:

    oo::class create Class {
        superclass oo::class
        constructor args {
            next {*}$args
            set cs [info class superclasses [self]]
            while {[llength $cs]} {
                set cs [concat [lassign $cs c] [info class superclasses $c]]
                oo::define [self] variable -append {*}[info class variables $c]
            }
        }
    }
    

    证明这一点:

    % Class create foo {
        variable x
    }
    ::foo
    % Class create bar {
        superclass foo
        variable y
    }
    ::bar
    % Class create boo {
        superclass bar
        variable z
    }
    ::boo
    % info class variables boo
    z y x
    

    虽然我一般不推荐这样做,因为当超类发展时它会使子类变得更加脆弱,并且它不会跟踪对超类的任何更改,但使用一点脚本就可以轻松设置。您只需将智能委派给您自己的元类,并使用它来构建您的所有操作类(从那里开始就是纯粹的传统 TclOO 类)。

    【讨论】:

      【解决方案2】:

      唐纳,感谢您的回答。

      所以我假设默认情况下没有可用的机制来使所有超类变量都可用。

      我目前的解决方案是收集所有变量名,然后通过一次调用来声明它们。 然而,我必须在每种方法中重复这一点。我想将 declareSuperclassVars 放在方法之外。 这有可能吗?也许用另一种方法?

      谢谢

      oo::class create toaster {
          variable nCrumbs;                    #declaration
          variable toTest;                     #another one nowhere defined
      
          constructor {} {
              set nCrumbs 0;                   #definition
          }
      
          method toast {nSlices} {
              if {$nCrumbs > 50} {
                  error "== FIRE! FIRE! =="
              }
              set nCrumbs [expr $nCrumbs+4*$nSlices]
          }
      
          method clean {} {
              set nCrumbs 0
          }
      
          method declareSuperclassVars {} {
              my variable lSuperclassVars
              set lSuperclassVars [info vars]; #fill variable list
              uplevel 1 {
                  my variable lSuperclassVars
                  eval "my variable $lSuperclassVars"
              }
          }
      }
      
      oo::class create smartToaster {
          superclass toaster;                  #inherit
      
          #declareSuperclassVars;              #<======= would like to do it here
      
          method toast {nSlices} {
              my declareSuperclassVars;        #declare all at once
      
              if {$nCrumbs > 40} {
                  my clean
              }
              next $nSlices; #call superclass method
          }
      }
      
      set clsToaster [smartToaster new]
      $clsToaster toast 2
      

      【讨论】:

      • 查看我对帖子的编辑。仍然认为这是一个坏主意,但很有可能。
      猜你喜欢
      • 2014-07-26
      • 2015-09-08
      • 2011-03-25
      • 2019-03-28
      • 1970-01-01
      • 2014-10-06
      • 2021-10-13
      • 2023-01-19
      • 1970-01-01
      相关资源
      最近更新 更多