注意:出于教学目的,以下所有示例均已简化。
关于方法
是的,你是对的。 new 函数的第一个参数,if 作为方法调用,将是你调用它的对象。
调用方法有两种“风格”,但结果都是一样的。一种风格依赖于运算符,二进制-> 运算符。另一种风格依赖于参数的顺序,即bitransitive verbs work in English。大多数人只在内置函数中使用与格/双及物风格——也许在构造函数中使用,但很少使用其他任何东西。
在大多数(但不是全部)情况下,前两个是等价的:
1. 与格方法调用
这是位置性的,它使用词序来确定发生了什么。
use Some::Package;
my $obj1 = new Some::Package NAME => "fred";
注意我们没有使用方法箭头:没有写的->。这就是 Perl 本身与许多自己的函数一起使用的,比如
printf STDERR "%-20s: %5d\n", $name, $number;
几乎每个人都喜欢同类产品:
STDERR->printf("%-20s: %5d\n", $name, $number);
然而,如今这种与格调用几乎只用于内置函数,因为人们总是把事情搞糊涂。
2。 箭头方法调用
箭头调用在很大程度上更清晰、更干净,不太可能让你陷入 Perl 解析奇怪的杂草中。注意我说的是不太可能;我确实没有说它没有任何不妥之处。但是为了回答这个问题,我们就假装这样吧。
use Some::Package;
my $obj2 = Some::Package->new(NAME => "fred");
在运行时,除非有任何奇特的奇怪或继承问题,否则实际的函数调用将是
Some::Package::new("Some::Package", "NAME", "fred");
例如,如果您在 Perl 调试器中并进行了堆栈转储,那么它的调用链中将包含与前一行类似的内容。
由于调用方法总是在参数列表前加上 invocant,所有将作为方法调用的函数都必须考虑到那个“额外”的第一个参数。这很容易做到:
package Some::Package;
sub new {
my($classname, @arguments) = @_;
my $obj = { @arguments };
bless $obj, $classname;
return $obj;
}
这只是调用构造函数的最常见的新方法的一个极其简化的示例,以及内部发生的情况。在实际的生产代码中,构造函数会更加小心。
方法和间接
有时你在编译时不知道类名或方法名,所以你需要使用一个变量来保存其中一个或另一个,或两者兼而有之。编程中的间接与自然语言中的间接对象不同。间接只是意味着您有一个包含其他内容的变量,因此您可以使用该变量来获取其内容。
print 3.14; # print a number directly
$var = 3.14; # or indirectly
print $var;
我们可以使用变量来保存方法调用中涉及的其他内容,而不仅仅是方法的参数。
3.使用间接方法名称的箭头调用:
如果你不知道方法名,那么你可以把它的名字放在一个变量中。仅使用箭头调用来尝试此操作,而不是与格调用。
use Some::Package;
my $action = (rand(2) < 1) ? "new" : "old";
my $obj = Some::Package->$action(NAME => "fido");
这里方法名称本身在运行时是未知的。
4.使用间接类名称的箭头调用:
这里我们使用一个变量来包含我们想要使用的类的名称。
my $class = (rand(2) < 1)
? "Fancy::Class"
: "Simple::Class";
my $obj3 = $class->new(NAME => "fred");
现在我们随机选择一类或另一类。
您实际上也可以通过这种方式使用与格调用:
my $obj3 = new $class NAME => "fred";
但这通常不是通过用户方法完成的。不过,有时它确实会发生在内置插件中。
my $fh = ($error_count == 0) ? *STDOUT : *STDERR;
printf $fh "Error count: %d.\n", $error_count;
这是因为尝试在与格槽中使用表达式通常不会在没有块的情况下起作用;否则它只能是一个简单的标量变量,甚至不能是数组或散列中的单个元素。
printf { ($error_count == 0) ? *STDOUT : *STDERR } "Error count: %d.\n", $error_count;
或者更简单地说:
print { $fh{$filename} } "Some data.\n";
真是太丑了。
让调用者小心
请注意,这并不完美。与格对象槽中的文字与那里的变量不同。例如,使用 literal 文件句柄:
print STDERR;
意思
print STDERR $_;
但是如果你使用 indirect 文件句柄,像这样:
print $fh;
这实际上意味着
print STDOUT $fh;
这不太可能意味着你想要的,可能是这样的:
print $fh $_;
又名
$fh->print($_);
高级用法:双重性质方法
关于方法调用箭头-> 的一点是,它不知道它的左侧操作数是表示类名的字符串还是表示对象实例的祝福引用。
当然,没有什么正式要求$class 包含包名。可能是两者之一,如果是,则由方法本身来做正确的事情。
use Some::Class;
my $class = "Some::Class";
my $obj = $class->new(NAME => "Orlando");
my $invocant = (rand(2) < 1) ? $class : $obj;
$invocant->any_debug(1);
这需要一个非常花哨的any_debug 方法,根据调用者是否受到祝福来做不同的事情:
package Some::Class;
use Scalar::Util qw(blessed);
sub new {
my($classname, @arguments) = @_;
my $obj = { @arguments };
bless $obj, $classname;
return $obj;
}
sub any_debug {
my($invocant, $value) = @_;
if (blessed($invocant)) {
$invocant->obj_debug($value);
} else {
$invocant->class_debug($value);
}
}
sub obj_debug {
my($self, $value) = @_;
$self->{DEBUG} = $value;
}
my $Global_Debug;
sub class_debug {
my($classname, $value) = @_;
$Global_Debug = $value;
}
但是,这是一种相当先进和微妙的技术,仅适用于少数不常见的情况。不建议在大多数情况下使用它,因为如果处理不当,可能会造成混淆——即使处理不当也是如此。