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

发布于 2025-01-09 10:55:09 字数 1597 浏览 0 评论 0原文

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

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

再次 - 为什么这个有效,以及为什么异常现在不同:私有常量 Foo::Bar未初始化的常量X::Bar

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

  1. 正是我想要的:Bar可以在内部访问,并且访问它会给出引用私有常量的异常。
  2. 第二个给出“ok”异常,但无法访问 Bar 本身
  3. 第三个可以访问,但现在给出略有不同的异常

这里到底发生了什么?

I have a simple module that defines a constant and makes it private:

module Foo
  Bar = "Bar"
  private_constant :Bar
end

I can include it in a class like this, and it works as expected:

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

(let's call it "typical class" definition)

Then I tried the Class.new approach, but this failed miserably:

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>'

Why? I always though class Something and Something = Class.new are equivalent. What's the actual difference?

Then I had a strike of inspiration, and recalled there's alternative way to define class methods, which actually worked:

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

Again - why this one work, and why the exception is now different: private constant Foo::Bar vs uninitialized constant X::Bar?

It seems like those 3 ways of initializing classes differ in a nuanced way.

  1. does exactly what I want: Bar is accessible internally, and accessing it gives exception about referencing private constant.
  2. second gives "ok" exception, but has no access to Bar itself
  3. third has access, but now gives slightly different exception

What is exactly going on in here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

╭ゆ眷念 2025-01-16 10:55:09

这是 Ruby 中最大的问题之一:常量定义范围部分是语法性的,也就是说,它取决于它周围的代码的结构。

module Foo
  Bar = "Bar"
end

Bar 位于 module 定义内,因此它是在该模块中定义的。

class << self
  include Foo
end

Bar 包含在 class 定义中,因此它是在该类中定义的。

Class.new do
  include Foo
end

没有封闭的classmodule(这是带有块的普通方法调用),因此常量是在顶层定义的。

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

This is one of the biggest gotchas in Ruby: constant definition scope is partially syntactic, that is, it depends on how the code around it is structured.

module Foo
  Bar = "Bar"
end

Bar is inside a module definition, so it is defined in that module.

class << self
  include Foo
end

Bar gets included inside a class definition, so it is defined in that class.

Class.new do
  include Foo
end

There is no enclosing class or module (this is a normal method call with a block), so the constant is defined at top level.

As for your third error, I believe that is because the constant got defined in the singleton class (that's what class << self is) versus the class itself. They are two separate class objects.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文