【问题标题】:When to use constants instead of instance variables in Ruby?何时在 Ruby 中使用常量而不是实例变量?
【发布时间】:2015-07-07 18:10:33
【问题描述】:

我知道实例变量是状态,常量是常量。是否有任何理由(除了约定)使用常量而不是实例变量?使用常量有内存/速度优势吗?

【问题讨论】:

  • 当然常量就是常量。这是一个重言式。这么写是没有意义的。
  • 鉴于您可以在 Ruby 中更改常量,因此它不是重言式。
  • 我明白了。您以两种不同的方式使用常量。

标签: ruby variables instance constants


【解决方案1】:

与实例变量不同,常量是全局的。如果您尝试重新分配他们的价值,他们至少会抱怨。

虽然在内存/速度方面可能存在理论上的差异,但在实践中将无关紧要。

【讨论】:

  • 常量只有在全局上下文中声明时才是全局的,否则它们的作用域照常。
  • 作用域常量仍然是全局变量,因为它们在运行时只存在一次(并且可以从任何地方访问)。另一方面,实例变量存在于每个对象中,并且只能通过它们所属的对象访问。
【解决方案2】:

这里有几点需要考虑:

  • 值会在对象的生命周期内发生变化吗?
  • 是否需要覆盖子类中的值?
  • 是否需要在运行时配置值?

最好的常量是那些不更新软件就不会真正改变的常量:

class ExampleClass
  STATES = %i[
    off
    on
    broken
  ].freeze
end

通常您在类内部使用这些常量并避免共享它们。当您共享它们时,它们的使用方式受到限制。例如,如果另一个类引用了ExampleClass::STATES,那么您无法在不更改其他代码的情况下更改该结构。

您可以通过提供一个接口来使其更加抽象:

class ExampleClass
  def self.states
    STATES
  end
end

如果您将来更改该常量的结构,您始终可以保留旧的行为:

class ExampleClass
  STATES = {
    on: 'On',
    off: 'Off',
    broken: 'Broken'
  }.freeze

  def self.states
    STATES.keys
  end
end

当您谈论实例变量时,您的意思是可以配置的东西:

class ConfigurableClass
  INITIAL_STATE_DEFAULT = :off

  def self.initial_state
    @initial_state || INITIAL_STATE_DEFAULT
  end

  def self.initial_state=(value)
    @initial_state = value ? value.to_sym
  end
end

常量的优点在于它们被定义一次并在整个过程中使用,因此从技术上讲它们更快。实例变量仍然很快,并且通常是如上所示的必需品。

【讨论】:

  • 你也可以使用Module#const_set:A.const_set('K','cat'); A.const_get('K') #=> "cat"
  • 是的,但是如果你试图重新定义一个常量,你会得到各种讨厌的警告,所以有额外的工作要取消定义,然后重新定义它,而不是仅仅分配一个变量。
  • 我不太确定常量是否真的更快在 Ruby 中,因为它们在技术上仍然是变量 - 您可以在运行时读取和写入它们。在编译语言中,编译器可以内联常量来加快速度,但在 Ruby 中这会更难。
  • @averell 任何类型的方法调用都会产生少量的损失,但随着每个版本的 Ruby 发布,这种损失正在减少。例如,由于内部优化,attr_reader 生成的方法比手动编码的等效方法要快。在大多数情况下,这种惩罚是无关紧要的,但值得了解。
  • @tadman - 我以为我们在谈论访问实例变量与访问常量,所以在这两种情况下都没有涉及方法调用。不过,我同意常量可能有一些优化潜力。
【解决方案3】:

您可能没有意识到这一点,但类和模块被视为常量。

pry(main)> Foo
NameError: uninitialized constant Foo

关于何时应该使用常量,我能给出的最佳建议是当它们完全是常量时。例如,如果我在 rails 中创建一个范围来查找所有最近的 Foos,我会创建一个常量来显示最近是什么。

class Foo < ActiveRecord::Base
  DAYS_TILL_OLD = 7.days

  scope :recent, -> { where "created_at > ?", DateTime.now - DAYS_TILL_OLD  }
end

【讨论】:

  • 不一定。模块(包括类)不一定是常量。 Class.new, a = Class.new, @a = Class.new, @@a = Class.new, $a = Class.new.
  • 类的实例被赋值给常量,但类本身并不是常量。
  • @sawa Class 仍然是一个常数。确定它可以分配给一个变量,但Class 类仍然是一个常量。 ruby-doc.org/core-2.2.1/Module.html#method-c-constants。 “在第一种形式中,返回从调用点可访问的所有常量名称的数组。此列表包括在全局范围内定义的所有模块和类的名称。”试试Object.constants 自己看看吧。
  • 类只是Class 类的实例。您可以拥有未分配给常量的类。 foo = Class.new 完全有效;然后,您可以使用 foo.new 对其进行实例化,而无需使用常量。
  • Class 类已分配给 Class 常量,是的。这并不意味着所有类都是常量。
猜你喜欢
  • 1970-01-01
  • 2010-10-28
  • 1970-01-01
  • 1970-01-01
  • 2020-11-01
  • 2015-08-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-21
相关资源
最近更新 更多