查看Type::Tiny
,我看到调用中的类名在Type::Tiny->new
官方文档中被引用,
my $NUM = "Type::Tiny"->new(
name => "Number",
constraint => sub { looks_like_number($_) },
message => sub { "$_ ain't a number" },
);
为什么是这样?这仅仅是风格吗?这种做法是否有任何性能影响?
查看Type::Tiny
,我看到调用中的类名在Type::Tiny->new
官方文档中被引用,
my $NUM = "Type::Tiny"->new(
name => "Number",
constraint => sub { looks_like_number($_) },
message => sub { "$_ ain't a number" },
);
为什么是这样?这仅仅是风格吗?这种做法是否有任何性能影响?
举一个更简单的例子
package Foo { sub new { die 7 } };
package Bar { sub new { die 42 } };
sub Foo { "Bar" }
Foo->new();
在上面的示例中,常量Foo
解析为“Bar”,因此调用"Bar"->new
not "Foo"->new
。你如何阻止子程序解析?你可以引用它。
"Foo"->new();
至于性能影响,使用字符串而不是裸词不会使事情变得更糟。我已经确认生成的 optreeO=Deparse
是相同的。因此,作为一般规则,如果您重视正确性,似乎最好引用类名。
这在 Programming Perl 中提到,(遗憾的是在间接方法调用的上下文中)
...所以我们会告诉你,只要有两件事是真的,你几乎总是可以用一个简单的类名逃脱。首先,没有与类同名的子程序。(如果您遵循子例程名称(如以
new
小写开头)和类名(如以ElvenRing
大写开头)的约定,这绝不是问题)。二、类被加载了一个use ElvenRing; require ElvenRing;
这些声明中的任何一个都确保 Perl 知道
ElvenRing
是一个模块名,这会强制new
将类名之前的任何裸名ElvenRing
解释为方法调用,即使您碰巧new
在当前包中声明了自己的子例程。
而且,这是有道理的:只有当您的子例程(通常是小写字母)与类(通常是大写字母)具有相同的名称时,才会发生这种混淆。仅当您违反上述命名约定时,才会发生这种情况。
tldr; 如果你知道它们并且你重视正确性而不是混乱,那么引用你的类名可能是一个好主意。
旁注:或者,您可以通过在函数末尾附加 a::
来停止将裸词解析为函数,例如在上面的Foo::->new
.
感谢reddit 上的 Ginnz 向我指出这一点,并感谢Toby Inkster 的评论(尽管在第一次阅读时对我来说没有意义)。
明确引用类名而不是使用裸词(被视为字符串)是避免语法歧义的三种方法之一。perlobj 文档的调用类方法部分解释了这一点。
因为 Perl 允许您对包名称和子例程名称使用裸词,所以它有时会错误地解释裸词的含义。例如,该构造
Class->new()
可以解释为'Class'->new()
或Class()->new()
。在英语中,第二种解释读作“调用名为 的子例程Class()
,然后调用new()
作为返回值的方法Class()
”。如果Class()
在当前命名空间中有一个命名的子例程,Perl 将总是解释Class->new()
为第二种选择:new()
对由调用返回的对象的调用Class()
。
通过下面的演示查看这个奇怪的案例。
#! /usr/bin/env perl
use strict;
use warnings;
sub Type::Tiny { print "Returning Bogus\n" ; return "Bogus" }
sub Type::Tiny::new { print "Type::Tiny::new\n" }
sub Bogus::new { print "Bogus::new\n" }
my $class = "Type::Tiny";
Type::Tiny->new;
Type::Tiny::->new;
"Type::Tiny"->new;
$class->new;
它的输出是
返回虚假 假的::新的 类型::小::新 类型::小::新 类型::小::新
上述文档部分的其余部分展示了如何防止意外行为或无意错误。
您可以通过两种方式强制 Perl 使用第一种解释(即,作为对名为 的类的方法调用
"Class"
)。首先,您可以将 a 附加::
到类名:Class::->new()
Perl 总是将其解释为方法调用。
或者,您可以引用类名:
'Class'->new()
当然,如果类名是标量 Perl 也会做正确的事:
my $class = 'Class'; $class->new();
适用于您的问题,以下所有调用都是等效的。
Type::Tiny::->new( … );
"Type::Tiny"->new( … );
my $class = "Type::Tiny";
$class->new( … );
附加::
到末尾具有产生有用警告的优点。假设您不小心输入了
Type::Tinny::->new;
那产生
裸词“Type::Tinny::”指的是 ./try 第 15 行不存在的包。 无法在 ./try 第 15 行通过包“Type::Tinny”(也许您忘记加载“Type::Tinny”?)找到对象方法“new”。