【问题标题】:How can I call methods on a tied variable?如何调用绑定变量的方法?
【发布时间】:2009-02-07 18:29:13
【问题描述】:

我刚刚开始了解领带。我有一个名为 Link 的类,我想做以下事情:

  • 如果获取,返回链接地址
  • 如果存储,存储新地址
  • 能够在其上调用方法

到目前为止,我的代码是:


package Link;

sub FETCH {
    my $this = shift;
    return $this->{"site"};
}

sub STORE {
    my ($self,$site) = @_;
    $self->{"site"}   = $site;
}

sub print_method {
    my $self = shift;
    print $self->{"site"};
}

sub TIESCALAR {
    my $class = shift;
    my $link  = shift;
    my $this  = {};
    bless($this,$class);
    $this->{"site"} = $link;
    return $this;
}

1;

我用来检查功能的代码是:


use Link;

tie my $var,"Link","http://somesite.com";
$var->print_method;

运行时,脚本将终止并出现以下错误: 如果没有 tietest.pl 第 4 行的包或对象引用,则无法调用方法“print_method”。

如果我正确理解它的信息,$var->print_method 会解析为调用方法 print_method 的某个字符串。我怎样才能从 tie 中受益,同时也将变量用作对象?

编辑:经过一番实验,我发现如果我在 fetch 上返回 $self ,我可以调用方法,但是 fetch 不会返回地址。

编辑 2:perl 僧侣为我提供了解决方案:tied。绑定将返回对对象 VARIABLE 的引用。

通过结合我的方法,我可以完成我想要的一切。

【问题讨论】:

    标签: perl object tie


    【解决方案1】:

    领带是这项工作的错误工具。当您想要与普通数据类型相同的接口但想要自定义操作如何工作时,您可以使用 tie。由于您想像标量一样访问和存储字符串,因此 tie 不会为您做任何事情。

    看起来您想要 URI 模块,或者它的子类,并且可能需要一些重载。

    如果你真的需要这样做,你需要使用正确的变量。 tie 将您指定的变量连接到您指定的类,但它仍然是一个普通的标量(而不是引用)。如果要调用方法,则必须使用它返回的对象:

    my $secret_object = tie my($normal_scalar), 'Tie::Class', @args;
    $secret_object->print_method;
    

    如果你只有绑定的标量,你也可以获得秘密对象:

    my $secret_object = tied $normal_scalar;
    

    我在Mastering Perl 有一整章关于领带。

    【讨论】:

      【解决方案2】:

      我建议制作一个普通的 Perl 对象,然后 overloading 字符串化。您失去了通过赋值存储值的能力,但保留了通过打印对象来获取值的能力。一旦你开始想要直接调用方法,对象可能就是你想要的。

      package Link;
      
      use strict;
      use Carp;
      
      use overload
      (
        '""'      => sub { shift->site },
         fallback => 1,
      );
      
      sub new 
      {
        my $class = shift;
      
        my $self = bless {}, $class;
      
        if(@_)
        {
          if(@_ == 1)
          {
            $self->{'site'} = shift;
          }
          else { croak "$class->new() expects a single URL argument" }
        }
      
        return $self;
      }
      
      sub site
      {
        my $self = shift;
        $self->{'site'} = shift  if(@_);
        return $self->{'site'};
      }
      
      sub print_method
      {
        my $self = shift;
        print $self->site, "\n";
      }
      
      1;
      

      示例用法:

      use Link;
      
      my $link = Link->new('http://somesite.com');
      
      print $link, "\n";   # http://somesite.com
      $link->print_method; # http://somesite.com
      

      如果您真的非常希望赋值也能正常工作,您可以将带有重载字符串化(Link,上图)的普通对象与 tie 结合起来:

      package LinkTie;
      
      use strict;
      use Link;
      
      sub FETCH
      {
        my $this = shift;
        return $this->{'link'};
      }
      
      sub STORE
      {
        my($self, $site) = @_;
        $self->{'link'}->site($site);
        return $site;
      }
      
      # XXX: You could generalize this delegation with Class::Delegation or similar
      sub print_method
      {
        my $self = shift;
        print $self->{'link'}->print_method;
      }
      
      sub TIESCALAR
      {
        my $class = shift;
        my $self = bless {}, $class;
        $self->{'link'} = Link->new(@_);
        return $self;
      }
      
      1;
      

      示例用法:

      tie my $link,'LinkTie','http://somesite.com';
      print $link, "\n";   # http://somesite.com
      $link->print_method; # http://somesite.com
      
      $link = 'http://othersite.com';
      
      print $link, "\n";   # http://othersite.com
      $link->print_method; # http://othersite.com
      

      这一切都非常可怕,而且还有很长的路要走才能获得可疑的能力来分配你也可以调用方法并按原样打印的东西。带有字符串化的标准 URI 对象可能是更好的选择。

      【讨论】:

      • 是的,但是分配给它,不会有我想要的效果。我问的不可能吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-10-21
      • 2020-10-24
      • 2023-03-19
      • 1970-01-01
      • 1970-01-01
      • 2011-05-07
      相关资源
      最近更新 更多