ruby - 定义类的不同方式如何影响包含作品的方式?

标签 ruby metaprogramming

我有一个简单的模块,它定义了一个常量并将其设为私有(private):

module Foo
  Bar = "Bar"
  private_constant :Bar
end

我可以将它包含在这样的类中,并且它按预期工作:

class User
  include Foo

  def self.test
    Bar
  end
end

puts User.test
# => Bar

begin
  User::Bar
rescue => exception
  puts "#{exception} as expected"
  # => private constant Foo::Bar referenced as expected
end

(我们称之为“典型类”定义)

然后我尝试了 Class.new 方法,但失败得很惨:

X = Class.new do
  include Foo

  def self.test
    Bar # Line 28 pointed in the stack trace
  end
end

begin
  X::Bar
rescue => exception
  puts "#{exception}"
  # => private constant Foo::Bar
end

puts X.test
# test.rb:28:in `test': uninitialized constant Bar (NameError)
#         from test.rb:28:in `<main>'

为什么?我一直认为 class SomethingSomething = Class.new 是等价的。实际区别是什么?

然后我灵机一动,想起了定义类方法的替代方法,它确实有效:

X = Class.new do
  class << self
    include Foo
    def test
      Bar
    end
  end
end

begin
  X::Bar
rescue => exception
  puts "#{exception}"
  # => uninitialized constant X::Bar
end

puts X.test
# Bar

同样 - 为什么这个有效,为什么异常现在不同了:private constant Foo::Bar vs uninitialized constant X::Bar?

这 3 种初始化类的方式似乎有细微差别。

  1. 完全符合我的要求:Bar 可在内部访问,访问它会给出引用私有(private)常量的异常。
  2. 第二个给出“ok”异常,但无权访问 Bar 本身
  3. 第三个可以访问,但现在给出略有不同的异常

这里到底发生了什么?

最佳答案

这是 Ruby 中最大的陷阱之一:常量定义范围是部分句法,也就是说,它取决于围绕它的代码的结构。

module Foo
  Bar = "Bar"
end

Barmodule里面定义,因此它在该模块中定义。

class << self
  include Foo
end

Bar包含在 class 中定义,所以它在那个类中定义。

Class.new do
  include Foo
end

没有封闭classmodule (这是一个带有 block 的普通方法调用),所以常量是在顶层定义的。

至于你的第三个错误,我认为这是因为常量是在单例类中定义的(这就是 class << self 是什么)而不是类本身。它们是两个独立的类对象。

关于ruby - 定义类的不同方式如何影响包含作品的方式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71221055/

相关文章:

ruby-on-rails - 导入一个css库

ruby - 使用 Grackle 的 Twitter OAuth 请求 token

ruby-on-rails - TinyTds 错误 : Adaptive Server connection timed out

C++检测是否存在按名称接受特定类型的函数

ruby-on-rails - Tomcat错误日志位置?

ruby-on-rails - Rails 3/Git/Bundler Fatal 无法解析对象

python - 使用内省(introspection)来查找对象的方法,然后进行过滤

ruby - 一种在 ruby​​ 中动态添加字段到结构的方法

c++ - 在编译时组合成员

c++ - 如何通过模板参数的可变列表向后迭代(递归)?