【问题标题】:Ruby class variables aren't auto-vivified?Ruby 类变量不会自动激活?
【发布时间】:2017-05-08 11:50:06
【问题描述】:

在开发 Ruby 程序时,我希望将一些状态数据从实例变量移动到类变量,但我突然意识到,虽然实例变量是自动激活的(如果您尝试在“不初始化”它们的情况下读取它们,它们是自动初始化为nil),类变量不是 - 这对我来说看起来很不一致(与大多数非常一致的Ruby语法相比)。

示例程序:

class Test
  def id
    @id.to_i
  end
  def id=(i)
    @id = i
  end
  def nextid
    self.id = id + 1
  end
end

t = Test.new
puts t.nextid #=> 1
puts t.nextid #=> 2

在这种情况下,当调用Test::id时,如果@id没有初始化,Ruby会自动将其激活为nil(之后我to_i它得到0)。

现在我决定要在 Test 实例之间共享运行 ID,因此我将其重写如下:

class Test
  def id
    @@id.to_i
  end
  def id=(i)
    @@id = i
  end
  def nextid
    self.id = id + 1
  end
end

t = Test.new
puts t.nextid
puts t.nextid

我想应该是一样的,但是不行:

NameError: uninitialized class variable @@id in Test

但是这种解决方法有效(!?):

class Test
  def id
    (@@id ||= 0).to_i
  end
  def id=(i)
    @@id = i
  end
  def nextid
    self.id = id + 1
  end
end

t = Test.new
puts t.nextid #=> 1
puts t.nextid #=> 2

(当然,在对0 进行惰性初始化后,我可以删除to_i,但为了保持一致性,我保留了它)。

看起来 Ruby 理解“延迟初始化”并将其视为不抛出 NameError 所需的魔法——即使 ||= 应该只是 x = x || val 的语法糖(顺便说一句,它不适用于初始化类变量,谢谢提问)。

怎么会?

【问题讨论】:

  • x ||= val 有点等价于x || x = val,而不是x = x || val。另外,在你的代码中,你为什么要一个实例方法来设置一个类变量?
  • 您测试的是哪个 ruby​​ 版本?我在 ruby​​-2.2.2 中尝试过第二种情况,我得到的是 NoMethodError: undefined method '+' for nil:NilClass 而不是 NameError: uninitialized class variable @@id
  • @fangxing:我不认为这取决于 Ruby 版本。我会说这是问题中的错字。您可以使用self.id = id + 1 来获取提到的错误。
  • ||= 可以很好地初始化类变量。你到底有什么问题?
  • @EricDuminil - re||= 是例如我不需要的变量。为什么类变量需要它而实例变量不需要?另外,如果x@@xx || x = val 也不会工作。

标签: ruby class-variables


【解决方案1】:

类变量初始化

这是possible explanation 为什么@anil@@aNameError

但是如果你想使用类变量,你应该在类内部初始化它们,而不是在实例方法内部:

class Test
  @@id = 0

  def id
    @@id
  end

  def id=(i)
    @@id = i
  end

  def nextid
    self.id = id + 1
  end
end

t = Test.new
puts t.nextid
puts t.nextid

请注意,为类变量设置实例设置方法没有多大意义。

类实例变量

为避免混合实例方法和类变量,您可以使用"class instance variable" 在类级别定义所有内容。它是在类级别定义的实例变量:

class Test
  @id = 0
  class << self
    def id
      @id
    end

    def id=(i)
      @id = i
    end

    def nextid
      self.id = id + 1
    end
  end
end

puts Test.id
# 0
puts Test.nextid
# 1
puts Test.nextid
# 2

这意味着你可以使用attr_accesor:

class Test
  @id = 0
  class << self
    attr_accessor :id
    def nextid
      self.id = id + 1
    end
  end
end

【讨论】:

  • 啊,抱歉。我实际上并没有读过那条线,而是被insta触发了。真丢人!
  • “另外,你声称 ||= 不适用于类变量,但它显然是错误的” - 我特别没有声称,请阅读 OP。问题是为什么它适用于类变量,而“写前读”却不行。
  • 谢谢,但我似乎没有指出,我对解决方案不感兴趣(因为我已经提出了一个解决方案,尽管它是一个丑陋的解决方案)但更多的是问题为什么事情是这样的。您指出的“可能的解释”基本上归结为“在以下边缘情况下有意义”,这几乎不是设计原因。
  • @Guss:我完全同意这不是一个完全令人满意的答案。这更像是我的调查记录。我什至不知道是否有明确的答案为什么 @@z 未定义。无论如何,我敢说最后一段代码是对代码的干净重构,没有任何类变量。无论如何,类变量已成为过去,不应在最近的项目中使用。
  • 您关于“类变量已弃用”的注释很有趣 - 愿意分享对 Ruby 社区相关讨论的引用吗?
猜你喜欢
  • 1970-01-01
  • 2010-12-02
  • 1970-01-01
  • 2017-05-15
  • 2020-11-24
  • 1970-01-01
  • 2014-03-06
  • 1970-01-01
  • 2015-08-26
相关资源
最近更新 更多