重载Perl对象以将哈希访问重定向到自定义例程

重载Perl对象以将哈希访问重定向到自定义例程,perl,overloading,Perl,Overloading,不久前,我编写了一个例程,它解析给定的字符串并以哈希(field=>value,field2=>value2)的形式返回一条记录。很好,除了需求已经改变,我现在需要返回更多数据,并提供getter方法来获取这些数据。因此,我调整了例程以返回一个记录对象,该对象在数据属性中存储相同的散列 但是,这将破坏期望散列的遗留代码,以便它可以使用$record->{field}获取数据。使用新的Record对象,此数据的路径现在是$Record->{data}->{field}或$Record->getBy

不久前,我编写了一个例程,它解析给定的字符串并以哈希
(field=>value,field2=>value2)的形式返回一条记录。很好,除了需求已经改变,我现在需要返回更多数据,并提供getter方法来获取这些数据。因此,我调整了例程以返回一个
记录
对象,该对象在
数据
属性中存储相同的散列

但是,这将破坏期望散列的遗留代码,以便它可以使用
$record->{field}
获取数据。使用新的
Record
对象,此数据的路径现在是
$Record->{data}->{field}
$Record->getByShortName('field')

我的想法是重载对象的FETCH方法并返回相应的字段。然而,这似乎不起作用。看来FETCH从未被调用过

我在寻找三条建议:

  • 如何正确地重载对象,使哈希访问尝试重定向到对象的自定义方法
  • 这是一个明智的工作方式还是会有一个巨大的速度惩罚
  • 在我的例子中,有没有更好的方法来保持向后兼容性
  • 这是一个MVE:

    录制.pm

    包装记录;
    严格使用;
    使用警告;
    使用数据::转储程序;
    使用重载回退=>1,'%{}'=>\&access\u散列;
    次新{
    我的($class,%args)=@;
    我的%fields=(答案=>42,问题=>21);
    $args{fields}=\%字段;
    返回{%args},$class;
    }
    子访问散列{
    我的($自我)=转变;
    return$self;#无法返回$self->{fields},因为这将无限递归
    }
    子提取{
    print(Dumper(@#))#不返回任何内容,是否未调用此方法
    }
    
    test.pl

    使用记录;
    my$inst=记录->新建();
    打印($inst->{answer}.\n”);
    打印($inst->{question}.\n”);
    
    记录
    是一个幸运的散列引用,因此如果您重载
    %{}
    运算符,则访问基础散列的字段时会遇到问题

    重载
    作者考虑到了这一点,并提供了
    重载
    pragma作为禁用此用例和其他一些用例的重载的方法

    use overload '%{}' => \&access_hash;
    ...
    sub access_hash {
        no overloading '%{}';
        my ($self) = shift;
        return $self->{fields};
    }
    

    在Perl 5.10之前,解决方法是通过临时将对象重新绑定到不会激活重载运算符的对象来禁用重载

    sub access_hash {
        my ($self) = shift;
        my $orig_ref = ref($self);
        bless $self, "#$%^&*()";
        my $fields = $self->{fields};
        bless $self, $orig_ref;
        return $fields;
    }
    

    Perl对象不一定需要专用的构造函数。您可以定义
    Record
    类,然后简单地
    返回$hashref,'Record'
    现在执行的操作
    返回$hashref。直接在hashref上运行的所有代码都将继续工作,但您也可以对其调用方法。

    FETCH
    是一种绑定变量的方法,这是一种与重载包不同的机制。hi@mob,这种访问哈希的方法确实有效。然而,这似乎是复杂的,伴随着多重祝福。会有(相对)巨大的速度惩罚吗?其次,当test.pl执行$class->{answer}时,我是否可以“捕获”它正在寻找的键?这里的键是“answer”。要捕获正在访问的键,您可以将
    $self->{fields}
    设置为绑定哈希(属于
    记录
    之外的单独类),谢谢。这很有帮助,我会尝试一下。但是,由于这将在循环中使用,因此
    access\u hash()
    中的代码将多次执行。就速度而言,使用上述代码有多糟糕?(顺便说一句,我使用的是Perl 5.8.8)@zdim就足够了。我重新措辞并纠正了错误。谢谢你帮我,帮你,帮我们大家。所以为了帮助每个人自助,我会继续说:我仍然不明白你的意思——可能只是我没有看到而已。他们没有
    return$hashref
    ——您是否引用了他们的
    access\u hash
    子例程(其中他们按名称
    $self
    返回hashref)?那么,你的意思是说他们可以(用数据填充
    $hashref
    )在那里祝福并返回,而不需要单独的子例程(
    new
    )来构造对象吗?在OPs代码库的某个地方,会生成并返回散列,然后再使用。(“编写了一个解析给定字符串并返回记录的例程”/
    $record->{data}->{field}
    )。把
    bless
    放到这个例程中,你就成功了。好吧,我明白你的意思了——将原始包放入类中(而不是传递对象等)。虽然这可能无法解决他们的问题,但事实上,这可能会简化整个问题。如果能更多地了解他们的代码,看看这是否可行,那就太好了。(我仍然觉得答案本身不清楚,但可能只有我一个人。)