2
class Something
    @@variable = 'Class variable'

    def give_me
        @@variable
    end
end

class OtherThing
    @variable = 'Instance variable with an interface'

    class << self
        attr_accessor :variable
    end

    def give_me
        self.class.variable
    end
end

p Something.new.give_me
p OtherThing.new.give_me

我想知道的是,我应该使用哪一个?各有什么好处和坏处?

类变量是:

  1. 私有的,除非你做一个接口
  2. 在继承之间共享
  3. 写得更短

类实例变量是:

  1. 公开的,因为您必须使用接口来访问它们
  2. 在继承之间不共享,但在继承时设置为 nil
  3. 更长的时间写

我还应该注意什么?

4

4 回答 4

3

我最近发现了 ActiveSupport 定义class_inheritable_accessor,它做了类实例变量所做的事情,其优点是对象不跨继承共享,并且在子类化时可以为变量设置默认值。

class Foo
  class_inheritable_accessor :x, :y
end

Foo.x = 1

class Bar < Foo
end

Bar.x #=> 1
Bar.x = 3
Bar.x #=> 3
Foo.x #=> 1

更多信息在这里

仅出于完整性考虑:在提供的两个选项中,我更喜欢使用类实例变量,因为这通常是预期的行为。

于 2011-08-16T05:15:35.347 回答
3

永远不要在 Ruby 中使用类变量。它们会导致继承问题。始终使用类实例变量。

于 2011-08-16T13:52:50.067 回答
1

您还可以选择在类级别声明实例变量:

class Foo
    @bar = 'baz'

    def Foo.print
        puts @bar
    end
end

总的来说,我会避免使用类变量,因为共享继承模型通常非常令人惊讶且不明显;老实说,我不确定他们真正提供了什么实用程序,除了不是一个全球性的全球性产品。

如果您需要一个“作用域”全局变量,我会选择带有访问器的类级实例变量。您避免了继承“惊喜”,同时仍然保持封装。

于 2011-08-16T00:20:44.180 回答
1

类实例变量通常比类变量更有用且不那么令人惊讶,因此您可能应该使用它们。

现在,我将详细说明您对 StackOverflow 上的 Ruby 答案所期望的令人痛苦的细节:

要声明或引用类实例变量,您需要在类范围内并使用单个 @ 符号。这会将变量放在该类的单例类实例上。

不幸的是,当您在类范围内并使用def关键字时,您的方法将放在该类的实例方法列表中,并在实例范围内执行,因此它们的 @-sign 变量将在它们所在的实例上.

相反,我们想要的是在类上定义方法,而不是在它的实例上。这真正意味着这些方法在类对象单例类实例方法列表中。(呸!)

所以,总结一下:在类对象的单例类上切换和定义方法至少有四种习语Foo

class Foo
  @a, @b, @c, @d = 1, 2, 3, 4

  # 1. pass the target to def
  def Foo.a
    @a
  end

  # 2. pass the target to def, relying on the fact that self 
  # happens to be the class object right now
  def self.b
    @b
  end

  # switch from class scope to singleton class scope
  class << self

    # 3. define a plain accessor in singleton class scope
    def c
      @c
    end

    # 4. use a macro to define an accessor
    attr_reader :d
  end

end

p [Foo.a, Foo.b, Foo.c, Foo.d]
#=> [1, 2, 3, 4]

(可能还有六种方法可以做到这一点,一旦你考虑到class_eval等等define_method,但现在应该让你满意。:-))

最后一点:类实例变量只能通过定义它们的类获得。如果您尝试从(或通过)子类调用这些方法中的任何一个,这些方法将执行,但 @ 变量将全部为零,因为self将是子类的类对象,而不是父类的类对象。

class Bar < Foo
end

p [Bar.a, Bar.b, Bar.c, Bar.d]
#=> [nil, nil, nil, nil]
于 2011-09-28T02:09:34.970 回答