2013-08-22 53 views
6

是否有可能获得特定Perl类的所有有效方法?是否有可能为特定的Perl类获取所有有效的方法?

我想操纵一个类的符号表并获得它的所有方法。我发现我可以通过$obj->can($method)从非子程序中分离出子程序,但这并不完全符合我的想法。

以下回报:

subroutine, Property, croak, Group, confess, carp, File 

然而,subroutine不是方法,(只是一个子程序),并croakconfess,并都导入到我的包。

我真正想要打印的是:

Property,Group, File 

但我会采取:

subroutine, Property,Group, File 

下面是我的程序:

#! /usr/bin/env perl 

use strict; 
use warnings; 
use feature qw(say); 

my $sections = Section_group->new; 
say join ", ", $sections->Sections; 

package Section_group; 
use Carp; 

sub new  { 
    return bless {}, shift; 
} 

sub Add { 
    my $self    = shift; 
    my $section    = shift; 
} 

sub Sections { 
    my $self    = shift; 

    my @sections; 
    for my $symbol (keys %Section_group::) { 
     next if $symbol eq "new"; # This is a constructor 
     next if $symbol eq "Add"; # Not interested in this method 
     next if $symbol eq "Sections";  # This is it's own method 
     push @sections, $symbol if $self->can($symbol); 
    } 

    return wantarray ? @sections : \@sections; 
} 

sub subroutine { 
    my $param1    = shift; 
    my $param2    = shift; 
} 

sub Group { 
    my $self    = shift; 
    my $section    = shift; 
} 

sub File { 
    my $self    = shift; 
    my $section    = shift; 
} 

sub Property { 
    my $self    = shift; 
    my $section    = shift; 
} 

回答

6

这是相当琐碎。我们只想保留最初在我们的软件包中定义的那些子名称。每个CV(代码值)都有一个指向定义它的包的指针。由于B,我们可以检查:

use B(); 

... 

if (my $coderef = $self->can($symbol)) { 
    my $cv = B::svref_2object $coderef; 
    push @sections, $symbol if $cv->STASH->NAME eq __PACKAGE__; 
} 

# Output as wanted 

也就是说,我们使用svref_2object进行反省。这将返回一个表示内部perl数据结构的Perl对象。

如果我们查看一个coderef,我们得到一个B::CV object,它代表内部CV。 CV中的STASH字段指向其定义的Stash。如您所知,Stash只是一个特殊的散列(内部表示为HV),因此$cv->STASH返回B::HVHVNAME字段包含HV为Stash且不是常规散列的Stash的完全限定软件包名称。

现在我们有我们需要的所有信息,并且可以比较想要的包名称和coderef的存储名称。

当然,这是简化的,你会想通过@ISA一般类的递归。


没有人喜欢污染的命名空间。值得庆幸的是,有一些模块可以从存储器中移除外来符号,例如namespace::clean。在编译时知道所有正在调用的子程序的CV时,这是没有问题的。

+3

然后,有时候人们实际上会从另一个包中导入一个方法(例如'使用Exporter'import';'而不是'@ISA = qw(Exporter);')。 – cjm

+0

@cjm当然,但我想这不会经常发生,甚至可能是反模式。希望大多数人能够从OOP(它解决了许多命名空间问题)中精心分离程序性编程(包括导入)。没有办法知道导入的sub是否意味着成为一种方法,所以这必须足够好。 (等等,也许我们可以访问':method'属性......) – amon

+0

_这很简单。不重要的?你在周末建造超快速发电机? Pervoc on ** svref_2object **:_引用任何Perl值,并将引用值转换为适当的B :: OP派生类或B :: SV派生类中的对象._我不知道什么这是在谈论。但是,它确实有效。我想现在是时候深入研究以前未发表的内容,并更多地改进我的Perl游戏。要么,要么学习Python。 –

6

你想做什么?为什么一个类如何定义或实现它所响应的方法?

Perl是一种动态语言,所以这意味着方法根本不必存在。与AUTOLOAD,一种方法可能是完美的,可调用的,但从不出现在符号表中。一个好的界面会使can在这些情况下工作,但可能会出现类或对象决定用false来响应的情况。

Package::Stash模块可以帮助您在特定命名空间中查找已定义的子例程,但正如您所说,它们可能未在同一个文件中定义。类中的方法可能来自继承的类。如果你关心他们来自哪里,你可能做错了。

+0

我有一个名为_Sections_的类,它表示我的控制文件中的各种节类型,它是Windows INI格式。每个部分都有参数,但参数对于每种类型的部分都不相同。 (目前有五个)。每种类型的部分都是部分的子类。我有另一个班,为我保存所有各个部分列表。每个部分类型在最后一个类中都有一个新的方法。这样,我对该INI文件的整个定义就在一个对象中。我班上的一种方法让我知道所有其他子类的名称。 –

+0

我曾经使用AUTOLOAD,但现在我避免它。使用'严格'使编程更好。使用参考结构可消除该安全性。我可能在一个地方有'$ foo - > {BAR}',另一个地方可能有'$ foo - > {BRA}',严格的'不能'接受。 OO放弃这种安全。如果我的方法是$ foo-> Bar,调用$ foo-> Bra会使我的程序崩溃。 AUTOLOAD打破了这种设计。两者现在都是真正的方法。它也适用于较差的整体设计。我可以用AUTOLOAD来做它。我不必思考问题。我的这个程序的prev版本使用了AUTOLOAD,由于AUTOLOAD,它很难调试。 –

+0

我会添加所有解释到这个问题:) –

相关问题