ruby 访问修饰符,不同版本的不同输出 2.5 - 2.7
Posted
技术标签:
【中文标题】ruby 访问修饰符,不同版本的不同输出 2.5 - 2.7【英文标题】:ruby access modifiers, different output with different versions 2.5 - 2.7 【发布时间】:2021-06-18 20:25:09 【问题描述】:我正在尝试运行此代码,它使用不同版本的 ruby 2.5 - 2.7 给出不同的输出
代码:
class ParentClass
def the_public_method
self.method1
end
private
def method1
puts "The private has been called"
end
end
class ChildClass < ParentClass
def test
self.method1
end
end
ParentClass.new.the_public_method
ChildClass.new.test
在 ruby 2.5 上提供:
Traceback (most recent call last):
1: from main.rb:19:in `<main>'
main.rb:3:in `the_public_method': private method `method1' called for
#<ParentClass:0x000056367ee0b388> (NoMethodError)
Did you mean? method
methods
exit status 1
在 ruby 2.7 上提供:
The private has been called
The private has been called
我认为旧版本的 ruby 的第一个输出是正确的。 有任何反馈吗?
【问题讨论】:
【参考方案1】:我认为旧版本的 ruby 的第一个输出是正确的。有任何反馈吗?
两个输出都是正确的。 Ruby 2.7 中的规范发生了变化,因此 Ruby 2.7 的行为自然会有所不同。
最初,私有方法的规则是“私有方法只能在没有显式接收者的情况下调用”。
然而,这意味着你不能使用私有的setter,因为foo = :bar
是一个局部变量赋值并且self.foo = :bar
是不允许的。
因此,规则已更改为“只能在没有显式接收者的情况下调用私有方法,但 setter 除外,其中允许文字伪变量 self
作为接收者”。
但是,这仍然没有考虑到诸如 self + 2
或 self.foo += 2
之类的事情,其中 +
、foo
或 foo=
是私有的,以及许多其他极端情况。
有一段时间,Ruby 开发人员试图通过忽略一些极端情况或添加一组更复杂的异常来解决这个问题,但实际上,解决方案相当简单:将规则更改为“私有方法”只能用字面伪变量self
作为显式或隐式接收器调用。
这是自 Ruby 2.7 以来的规则。
【讨论】:
不幸的是,角落案例obj = self ; obj.foo
仍然失败。
所有这些定义都有一个共同点,那就是可以对它们进行语法检查。几个实现者(我个人知道 JRuby 和 TruffleRuby)强烈希望保留这个属性,自从 Ruby 中存在私有方法以来,这一直是事实。始终要求只有文字伪变量self
可以用作私有方法的显式接收器。否则,你会遇到像def foo; if rand < 0.5 then self else Object.new end end; foo.bar
这样的问题,现在如果bar
是私有的,是否允许?
是的,您必须在调用时进行检查。但我不明白为什么这是一件坏事。【参考方案2】:
Ruby 2.7 allows calling a private method with self
在 Ruby 2.7 之前,允许使用字面量 self 作为接收者调用私有写入/赋值方法,但使用 self 调用任何其他私有方法会引发 NoMethodError 错误。
Ruby 2.7 旨在标准化 self 和 private 方法之间的交互。上述不一致已在 Ruby 2.7 中得到修复。
所以在 Ruby 2.7 之前第一个输出是正确的,在 Ruby 2.7 之后第二个输出是正确的。
【讨论】:
【参考方案3】:在 Ruby 中,private
方法仍然可以从继承的类中访问,但过去需要非显式接收(即隐式调用,如 mehtod1
但不是 obj.method1
或 self.method1
)
正如@eux 所说,最后一个要求已经放宽了in ruby 2.7,因此您现在也可以致电self.method
。
Ruby 中可见性的另一个怪癖是它链接到实例而不是类本身。这解释了private
的行为,让你理解下面的代码:
class Foo
def initialize(name)
@name = name
end
def ==(rhs)
name == rhs.name
end
private
attr_reader :name
end
f = Foo.new("bar")
f == f # NoMethodError
这里出现 NoMethodError 是因为 attr_reader :name
是私有的,所以你不能访问 另一个对象 的方法 name
。要启用此行为,请使用 protected
【讨论】:
以上是关于ruby 访问修饰符,不同版本的不同输出 2.5 - 2.7的主要内容,如果未能解决你的问题,请参考以下文章