5

我有一个 Perl 程序,它使用我从另一个来源获得的包。该方法的功能之一返回一个未知类的对象,有没有办法让我在不查看其类实现的情况下获取对象的所有可能方法?

4

2 回答 2

9

并不真地。

TL;博士:

  • 您可以找到显式声明或放置在对象类的名称空间中的子例程的名称。

  • 您无法区分这些子例程中的哪些是对象上的对象方法,哪些是类或非对象子程序(这是列出的最严重的问题/限制)。

  • 您无法使用此方法找到子类中的对象从超类继承的方法,除非它们已经在您的对象上调用过。

    这可以通过检查@ISA类来构建继承树或使用适当的 CPAN 模块来编写。

  • 您找不到动态添加到类中的方法(AUTOLOAD,在某处的代码中手动注入方法)。

详细地

  1. 您可以找到该类中的所有子例程(通过结合类名称空间是一个散列的事实,因此其中的所有标识符都是该散列中的键;以及UNIVERSAL::can对单独子例程的调用)。

    因此,如果您保证(通过非技术合同)类中 100% 的子例程是对象方法,并且您的类不是子类,您可以找到它们的列表。

    package MyClass;
    use vars qw($z5);
    my $x = 11; our $y = 12; $z5 = 14; %z2 = (1=>2); # my, our, globals, hash
    sub new { return bless({}, $_[0]) }; # Constructor
    sub x1 { my $self = shift; print $_[0]; };
    sub y2  { my $self = shift; print $_[0]; };
    ##############################################################################
    package MySubClass;
    use vars qw(@ISA);
    @ISA = ("MyClass");
    sub z3 { return "" };
    ##############################################################################
    package main;
    use strict; use warnings;
    
    my $obj = MyClass->new();
    list_object_methods($obj);
    my $obj2 = MySubClass->new();
    list_object_methods($obj2);
    $obj2->x1();
    list_object_methods($obj2); # Add "x1" to the list!
    
    sub list_object_methods {
        my $obj = shift;
        my $class_name = ref($obj);
        no strict;
        my @identifiers = keys %{"${class_name}::"};
        use strict;
        my @subroutines = grep { UNIVERSAL::can($obj, $_) } @identifiers;
        print "Class: ${class_name}\n";
        print "Subroutines: \n=========\n"
            . join("\n", sort @subroutines) . "\n=========\n";
    }
    

    ... 印刷:

    Class: MyClass
    Subroutines:
    =========
    new
    x1
    y2
    =========
    Class: MySubClass
    Subroutines:
    =========
    new
    z3
    =========
    Class: MySubClass
    Subroutines:
    =========
    new
    x1
    z3
    =========
    

    请注意,第一次打印的列表(对于 MySubClass)newz3但不是x1-y2因为new已执行并z3在类中声明;但x1两者y2都不是——它们只是理论上继承的。但是,一旦我们执行了一个继承的x1方法,那么第二次列表包含它,但仍然缺少继承y2的。


  2. 但不幸的是,你不能区分一个子程序是对象方法(例如,将它获得的第一个参数视为对象)、类方法(例如,将它获得的第一个参数视为类名)或非 OO 子程序(将第一个参数视为常规参数)。

    要区分这 3 个,唯一的方法是实际从语义上分析代码。否则,您无法区分:

    sub s_print_obj {
        my ($self, $arg1) = @_;
        $s->{arg1} = $arg1;
        print "$arg1\n";
    }
    # $obj->s_print_obj("XYZ") prints "XYZ" and stores the data in the object
    
    sub s_print_class {
        my ($class, $arg1) = @_; 
        print "Class: $class\n";
        print "$arg1\n";
    }
    # $obj->s_print_class("XYZ") prints "Class: MyClass\nXYZ\n"
    
    sub s_print_static {
        my ($self, $arg1) = @_;
        print "$arg1\n";
    }
    # $obj->s_print_static("XYZ") prints stringified representation of $obj
    

    注意:事实上,有些人实际上编写了他们的类的方法——那些可以以这种方式工作的方法——以明确地在所有 3 种(或前 2 种)情况下工作,无论该方法如何被调用。

于 2012-06-03T10:54:08.800 回答
0

DVK 的回答是准确的,但有点冗长。简短的回答是可以,但您不知道什么是公共对象方法,什么不是。从其他模块导入的私有方法和函数可能会出现。

获取可调用的具体(即非自动加载)方法列表的最简单方法是使用perl5i 元对象methods()方法。

use perl5i::2;

my $object = Something::Something->new;
my @methods = $object->mo->methods;

这至少消除了很多代码。

于 2012-06-03T17:22:48.747 回答