【问题标题】:Ruby constants in singleton methods单例方法中的 Ruby 常量
【发布时间】:2021-06-24 20:13:55
【问题描述】:

我有一个 Ruby C 扩展;我将ext/foo/foo.c 中定义的类FooC 称为我拥有的那个文件中

void Init_foo(void)
{
  VALUE cFoo = rb_const_get(rb_cObject, rb_intern("FooC"));
  :
  rb_define_const(cFoo, "BAR", 7);
  :

这包含在lib/foo.rb 中的扩展的“Ruby 部分”中

class FooC ; end

require 'foo/foo'

class Foo < FooC
  class << self
    def puts_bar
      puts BAR
    end
    :
  end
  :
end

没想到,我得到一个错误

NameError: uninitialized constant #<Class:Foo>::BAR

我原以为 Ruby 不会在 Foo 命名空间中找到 BAR,但会搜索 FooC 找到它的位置。奇怪的是,如果我使用Foo::BARself::BAR,错误就会消失,而Foo instance 方法中的类似用法根本不会给我任何错误。

有人知道为什么我需要在单例方法中对常量进行这种看似多余的限定吗?

[编辑]

根据下面有趣的答案,我确认在适当的类方法中不需要此限定:

class Foo < FooC
  def self.puts_bar
    # no NameError here
    puts BAR
  end
  :
end

而不是特征类的实例方法。

【问题讨论】:

    标签: ruby ruby-c-extension


    【解决方案1】:

    您在Foo 的特征类中查找常量,而不是在Foo 本身。首先弄清楚没有任何 绑定的情况。

    class FooC; end
    class Foo < FooC
      BAR = 42
      puts "Foo: #{self}, DEF: #{const_defined?(:BAR)}"
      class << self
        def puts_bar
          puts "Foo.class: #{self} DEF: #{self.class.const_defined?(:BAR)}"
          puts self.class.const_get(:BAR)
        end
      end
      def puts_bar
        puts "Foo.class: #{self} DEF: #{self.class.const_defined?(:BAR)}"
        puts self.class.const_get(:BAR)
      end
    end
    

    看看它是如何工作的:

    [2] pry(main)> Foo.puts_bar
    #⇒ Foo.class: Foo DEF: false
    #   NameError: uninitialized constant Class::BAR
    #   from (pry):8:in `const_get'
    [3] pry(main)> Foo.new.puts_bar
    #⇒ Foo.class: #<Foo:0x0000556e174b91c0> DEF: true
    #   42
    

    通过查找类的常量,常量在实例函数中可见。 #&lt;Class:Foo&gt; 在您收到的错误消息中明确指出了这一点。

    【讨论】:

    • 啊,我在想这与C有关,我发现在特征类中定义一个“正确”的类方法而不是实例方法,那么这种奇怪的行为就不会发生.我仍然不完全清楚为什么......我已经稍微编辑了这个问题。
    • class &lt;&lt; self 引入了一个新范围,def 没有。也就是说,BAR 在您编辑的版本中可见,因为关闭了。
    • 欢迎回到 ruby​​。 :) 从技术上讲,BAR=42 不应该在基于 C 扩展代码的FooC 中吗?
    • @engineersmnky 谢谢 :) 确实,我被所有这些cFooFooC 命名冲突所困。尽管如此,要从 eigenclass 访问常量,就像在示例中一样,应该完全限定它,而实例对它具有非限定访问权限。
    • 同意影响是一样的,虽然class Foo &lt; FooC; class &lt;&lt; self; def puts_bar; const_get(:BAR); end; end; end; 会起作用,但只是BAR 不会(如 OP 中所示)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-09
    • 1970-01-01
    • 2011-08-27
    • 2014-03-06
    • 1970-01-01
    • 2013-05-28
    • 1970-01-01
    相关资源
    最近更新 更多