如何从它们所属的类型之外访问私有方法或属性?
How do you access private methods or attributes from outside the type they belong to?
在极少数情况下这实际上是可以接受的,例如在单元测试中,您可能想要获取或设置私有属性的值,或者调用不应该执行的类型的私有方法.真的不可能吗?如果没有,你怎么能做到?
您可以通过两种方式访问某个类型的私有方法,以及一种获取私有属性的方式。都需要元编程,除了第一种方式调用私有方法,反正解释还是元编程。
例如,我们将实现 Hidden
class 使用私有属性隐藏值,以及 Password
class 使用 Hidden
存储密码。 请勿将此示例复制到您自己的代码中。这不是您合理处理密码的方式;这只是为了举例。
调用私有方法
信任其他 classes
Metamodel::Trusting
是实现高阶工作所需行为的元角色(类型的类型,或种类,从这里开始称为 HOW)能够信任其他类型。 Metamodel::ClassHOW
(classes,并通过扩展,语法)是 Rakudo 内置的唯一执行此角色的方法。
trusts
是一个关键字,可以在包内使用它来允许另一个包调用它的私有方法 (这不包括私有属性)。例如,使用 trusts
:
粗略地实现密码容器 class 可能看起来像这样
class Password { ... }
class Hidden {
trusts Password;
has $!value;
submethod BUILD(Hidden:D: :$!value) {}
method new(Hidden:_: $value) {
self.bless: :$value
}
method !dump(Hidden:D: --> Str:D) {
$!value.perl
}
}
class Password {
has Hidden:_ $!value;
submethod BUILD(Password:D: Hidden:D :$!value) {}
method new(Password:_: Str:D $password) {
my Hidden:D $value .= new: $password;
self.bless: :$value
}
method !dump(Password:D: --> Str:D) {
qc:to/END/;
{self.^name}:
$!value: {$!value!Hidden::dump}
END
}
method say(Password:D: --> Nil) {
say self!dump;
}
}
my Password $insecure .= new: 'qwerty';
$insecure.say;
# OUTPUT:
# Password:
# $!value: "qwerty"
#
使用 ^find_private_method 元方法
Metamodel::PrivateMethodContainer
是一个元角色,它实现了应该能够包含私有方法的 HOW 的行为。 Metamodel::MethodContainer
和 Metamodel::MultiMethodContainer
是实现方法行为的其他元角色,但此处不予讨论。 Metamodel::ClassHOW
(classes,通过扩展,语法)、Metamodel::ParametricRoleHOW
和 Metamodel::ConcreteRoleHOW
(角色)和 Metamodel::EnumHOW
(枚举)是 Rakudo 内置的 HOW做这个角色。 Metamodel::PrivateMethodContainer
的方法之一是 find_private_method
,它以一个对象和一个方法名称作为参数,当找到 none 时,或者 returns Mu
,或者Method
表示您正在查找的方法的实例。
密码示例可以重写为不使用 trusts
关键字,方法是删除使 [=13=] 信任 Password
的行并将 Password!dump
更改为:
method !dump(Password:D: --> Str:D) {
my Method:D $dump = $!value.^find_private_method: 'dump';
qc:to/END/;
{self.^name}:
$!value: {$dump($!value)}
END
}
获取和设置私有属性
Metamodel::AttributeContainer
是实现应包含属性的类型的行为的元角色。与方法不同,这是处理所有类型属性所需的唯一元角色。在 Rakudo 内置的 HOW 中,Metamodel::ClassHOW
(classes,并通过扩展,语法),Metamodel::ParametricRoleHOW
和 Metamodel::ConcreteRoleHOW
(角色),Metamodel::EnumHOW
(枚举)和 Metamodel::DefiniteHOW
(在内部使用,因为值 self
绑定到 public 属性的访问器方法中)执行此角色。
添加到 HOW 的元方法之一 Metamodel::AttributeContainer
是 get_attribute_for_usage
,给定对象和属性名称,如果没有找到属性则抛出,否则 returns Attribute
表示您正在查找的属性的实例。
Attribute
是 Rakudo 内部存储属性的方式。我们在这里关心的 Attribute
的两个方法是 get_value
,它接受一个包含 Attribute
实例和 returns 它的值的对象,以及 set_value
,它接受一个包含 Attribute
实例和一个值的对象,并设置它的值。
可以重写密码示例,因此 Hidden
不会像这样实现 dump
私有方法:
class Hidden {
has $!value;
submethod BUILD(Hidden:D: :$!value) {}
method new(Hidden:_: $value) {
self.bless: :$value;
}
}
class Password {
has Hidden:_ $!value;
submethod BUILD(Password:D: Hidden:D :$!value) {}
method new(Password:_: Str:D $password) {
my Hidden:D $value .= new: $password;
self.bless: :$value
}
method !dump(Password:D: --> Str:D) {
my Attribute:D $value-attr = $!value.^get_attribute_for_usage: '$!value';
my Str:D $password = $value-attr.get_value: $!value;
qc:to/END/;
{self.^name}:
$!value: {$password.perl}
END
}
method say(Password:D: --> Nil) {
say self!dump;
}
}
my Password:D $secure .= new: 'APrettyLongPhrase,DifficultToCrack';
$secure.say;
# OUTPUT:
# Password:
# $!value: "APrettyLongPhrase,DifficultToCrack"
#
F.A.Q.
{ ... }
是做什么的?
这会存根一个包,允许您在实际定义它之前声明它。
qc:to/END/
是做什么的?
您可能以前见过 q:to/END/
,它允许您编写多行字符串。在 :to
之前添加 c
允许在字符串中嵌入闭包。
为什么语法 class 是扩展名?
语法使用 Metamodel::GrammarHOW
,它是 Metamodel::ClassHOW
的子class。
你说^find_private_method和^get_attribute_for_usage第一个参数是一个对象,你在例子中省略了。为什么?
在对象上调用元方法将其自身作为第一个参数隐式传递。如果我们直接在对象的 HOW 上调用它们,我们会将对象作为第一个参数传递。
在极少数情况下这实际上是可以接受的,例如在单元测试中,您可能想要获取或设置私有属性的值,或者调用不应该执行的类型的私有方法.真的不可能吗?如果没有,你怎么能做到?
您可以通过两种方式访问某个类型的私有方法,以及一种获取私有属性的方式。都需要元编程,除了第一种方式调用私有方法,反正解释还是元编程。
例如,我们将实现 Hidden
class 使用私有属性隐藏值,以及 Password
class 使用 Hidden
存储密码。 请勿将此示例复制到您自己的代码中。这不是您合理处理密码的方式;这只是为了举例。
调用私有方法
信任其他 classes
Metamodel::Trusting
是实现高阶工作所需行为的元角色(类型的类型,或种类,从这里开始称为 HOW)能够信任其他类型。 Metamodel::ClassHOW
(classes,并通过扩展,语法)是 Rakudo 内置的唯一执行此角色的方法。
trusts
是一个关键字,可以在包内使用它来允许另一个包调用它的私有方法 (这不包括私有属性)。例如,使用 trusts
:
class Password { ... }
class Hidden {
trusts Password;
has $!value;
submethod BUILD(Hidden:D: :$!value) {}
method new(Hidden:_: $value) {
self.bless: :$value
}
method !dump(Hidden:D: --> Str:D) {
$!value.perl
}
}
class Password {
has Hidden:_ $!value;
submethod BUILD(Password:D: Hidden:D :$!value) {}
method new(Password:_: Str:D $password) {
my Hidden:D $value .= new: $password;
self.bless: :$value
}
method !dump(Password:D: --> Str:D) {
qc:to/END/;
{self.^name}:
$!value: {$!value!Hidden::dump}
END
}
method say(Password:D: --> Nil) {
say self!dump;
}
}
my Password $insecure .= new: 'qwerty';
$insecure.say;
# OUTPUT:
# Password:
# $!value: "qwerty"
#
使用 ^find_private_method 元方法
Metamodel::PrivateMethodContainer
是一个元角色,它实现了应该能够包含私有方法的 HOW 的行为。 Metamodel::MethodContainer
和 Metamodel::MultiMethodContainer
是实现方法行为的其他元角色,但此处不予讨论。 Metamodel::ClassHOW
(classes,通过扩展,语法)、Metamodel::ParametricRoleHOW
和 Metamodel::ConcreteRoleHOW
(角色)和 Metamodel::EnumHOW
(枚举)是 Rakudo 内置的 HOW做这个角色。 Metamodel::PrivateMethodContainer
的方法之一是 find_private_method
,它以一个对象和一个方法名称作为参数,当找到 none 时,或者 returns Mu
,或者Method
表示您正在查找的方法的实例。
密码示例可以重写为不使用 trusts
关键字,方法是删除使 [=13=] 信任 Password
的行并将 Password!dump
更改为:
method !dump(Password:D: --> Str:D) {
my Method:D $dump = $!value.^find_private_method: 'dump';
qc:to/END/;
{self.^name}:
$!value: {$dump($!value)}
END
}
获取和设置私有属性
Metamodel::AttributeContainer
是实现应包含属性的类型的行为的元角色。与方法不同,这是处理所有类型属性所需的唯一元角色。在 Rakudo 内置的 HOW 中,Metamodel::ClassHOW
(classes,并通过扩展,语法),Metamodel::ParametricRoleHOW
和 Metamodel::ConcreteRoleHOW
(角色),Metamodel::EnumHOW
(枚举)和 Metamodel::DefiniteHOW
(在内部使用,因为值 self
绑定到 public 属性的访问器方法中)执行此角色。
添加到 HOW 的元方法之一 Metamodel::AttributeContainer
是 get_attribute_for_usage
,给定对象和属性名称,如果没有找到属性则抛出,否则 returns Attribute
表示您正在查找的属性的实例。
Attribute
是 Rakudo 内部存储属性的方式。我们在这里关心的 Attribute
的两个方法是 get_value
,它接受一个包含 Attribute
实例和 returns 它的值的对象,以及 set_value
,它接受一个包含 Attribute
实例和一个值的对象,并设置它的值。
可以重写密码示例,因此 Hidden
不会像这样实现 dump
私有方法:
class Hidden {
has $!value;
submethod BUILD(Hidden:D: :$!value) {}
method new(Hidden:_: $value) {
self.bless: :$value;
}
}
class Password {
has Hidden:_ $!value;
submethod BUILD(Password:D: Hidden:D :$!value) {}
method new(Password:_: Str:D $password) {
my Hidden:D $value .= new: $password;
self.bless: :$value
}
method !dump(Password:D: --> Str:D) {
my Attribute:D $value-attr = $!value.^get_attribute_for_usage: '$!value';
my Str:D $password = $value-attr.get_value: $!value;
qc:to/END/;
{self.^name}:
$!value: {$password.perl}
END
}
method say(Password:D: --> Nil) {
say self!dump;
}
}
my Password:D $secure .= new: 'APrettyLongPhrase,DifficultToCrack';
$secure.say;
# OUTPUT:
# Password:
# $!value: "APrettyLongPhrase,DifficultToCrack"
#
F.A.Q.
{ ... }
是做什么的?
这会存根一个包,允许您在实际定义它之前声明它。
qc:to/END/
是做什么的?
您可能以前见过 q:to/END/
,它允许您编写多行字符串。在 :to
之前添加 c
允许在字符串中嵌入闭包。
为什么语法 class 是扩展名?
语法使用 Metamodel::GrammarHOW
,它是 Metamodel::ClassHOW
的子class。
你说^find_private_method和^get_attribute_for_usage第一个参数是一个对象,你在例子中省略了。为什么?
在对象上调用元方法将其自身作为第一个参数隐式传递。如果我们直接在对象的 HOW 上调用它们,我们会将对象作为第一个参数传递。