別のソースから入手したパッケージを使用するPerlのプログラムがあります。メソッドの関数の1つは、未知のクラスのオブジェクトを返します。クラスの実装を見ずに、オブジェクトのすべての可能なメソッドを取得する方法はありますか?
2 に答える
あまり。
TL; DR:
オブジェクトのクラスの名前空間に明示的に宣言または配置されたサブルーチンの名前を見つけることができます。
これらのサブルーチンのどれがオブジェクトのオブジェクトメソッドであり、どれがクラスまたは非オブジェクトサブルーチンであるかを区別することはできません(これは、リストされているものの中で最も深刻な問題/制限です)。
オブジェクトで既に呼び出されていない限り、このメソッドを使用してスーパークラスからサブクラス内のオブジェクトによって継承されたメソッドを見つけることはできません。
@ISA
これは、クラスを調べて継承ツリーを構築するか、適切なCPANモジュールを使用することでコーディングできます。クラスに動的に追加されるメソッドが見つかりません(AUTOLOAD、コード内の手動メソッドインジェクションのどこかにあります)。
詳細に
そのクラスのすべてのサブルーチンを見つけることができます(クラスの名前空間がハッシュであるため、その中のすべての識別子がそのハッシュのキーであるという事実と、
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の場合)は印刷され
new
ますが、実行されてクラスで宣言されたため、または-ではz3
ないことに注意してください。しかし、どちらでもありませんでした-それらは単に理論的に継承されただけです。しかし、継承されたメソッドを実行すると、2回目のリストにはそれが含まれていましたが、継承されたメソッドはまだありません。x1
y2
new
z3
x1
y2
x1
y2
ただし、残念ながら、オブジェクトメソッド(たとえば、取得した最初の引数をオブジェクトとして扱う)、クラスメソッド(たとえば、取得した最初の引数をクラス名として扱う)、または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つ)の場合に明示的に機能するように、クラスのメソッド(このように機能するメソッド)を実際に作成する人もいます。
DVKの答えは正確ですが、少し長いです。簡単な答えは「はい」ですが、パブリックオブジェクトメソッドとして何が意図されていて、何が意図されていなかったかはわかりません。他のモジュールからインポートされたプライベートメソッドと関数が表示される場合があります。
呼び出し可能な具体的な(つまり、非AUTOLOAD)メソッドのリストを取得する最も簡単な方法は、perl5iメタオブジェクトのmethods()メソッドを使用することです。
use perl5i::2;
my $object = Something::Something->new;
my @methods = $object->mo->methods;
それは少なくとも多くのコードを排除します。