为什么有些用户在Perl中引用类名?


12

看一下Type::Tiny,我看到Type::Tiny->new在的文档中引用了对的调用中的类名,

my $NUM = "Type::Tiny"->new(
   name       => "Number",
   constraint => sub { looks_like_number($_) },
   message    => sub { "$_ ain't a number" },
);

为什么是这样?这仅仅是样式吗?这种做法是否会对性能产生影响?

Answers:


7

举一个简单的例子

package Foo { sub new { die 7  } };
package Bar { sub new { die 42 } };
sub Foo { "Bar" }
Foo->new();

在上面的示例中,常量Foo解析为“ Bar”,因此该调用"Bar"->new不是"Foo"->new。如何停止解决子程序?您可以引用它。

"Foo"->new();

至于性能含义,使用字符串而不是裸字不会使情况变得更糟。我已经确认生成的optree O=Deparse是相同的。因此,作为一般规则,如果您重视正确性,似乎最好引用Classnames。

在Perl编程中对此进行了提及(在间接方法调用的上下文中很可悲)

...因此,我们将告诉您,只要有两件事是对的,您几乎总是可以使用裸类名。首先,没有与该类同名的子例程。(如果遵循惯例,则子例程名称(例如new开始小写)和类名称(例如ElvenRing开始大写)将永远不会出现问题。第二,该类已加载以下内容之一

use ElvenRing;
require ElvenRing;

这些声明中的任何一个都可以确保Perl知道ElvenRing是一个模块名称,即使您在当前包中恰好声明了自己的子例程,也可以将诸如new类名之前的任何裸名都强制ElvenRing解释为方法调用new

而且,这是有道理的:仅当您的子例程(通常为小写)与类具有相同的名称(通常为大写)时,才会发生混淆。仅当您违反上述命名约定时,才会发生这种情况。

tldr; 如果您知道类名并且重视混乱的正确性,则最好引用类名。

旁注:或者,您可以通过在功能::的末尾附加a 来停止对某个功能的裸字解析,例如在上文中Foo::->new


感谢Ginnz在reddit上向我指出的这一点,并感谢Toby Inkster的评论(尽管在初读时对我没有意义)。


2
或者Foo::->new,正如我从池上中学到的。
暴徒

@mob是的,Perl书中也(明确地)提到了这一点,不确定我是否应该放在那儿?大声笑。
埃文·卡罗尔

@mob我也将其添加为脚注。虽然我不确定我是否喜欢它。
埃文·卡罗尔

1
Foo::优点是可以警告是否Foo不是现有软件包
ikegami

1
Try :: Tiny是比Foo更好的例子。在您使用的代码中Foo->,可以预期您知道程序包中没有此类子例程。但是,如果您使用的是Try :: Tiny和其他许多cpan /其他外部来源的模块,谁会说其中一个是否正在使用Try程序包,如果是,则其中是否包含Tiny子程序?
ysth

0

显式引用类名而不是使用裸字(被视为字符串)是避免语法歧义的三种方法之一。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")。首先,您可以::在类名称后附加一个:

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”。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.