我应该在Ruby中使用类变量或类实例变量用于类静态变量吗?

时间:2011-08-16 00:16:02

标签: ruby class-variables class-instance-variables

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. 写作时间更短
  4. 类实例变量是:

    1. 公开,因为您必须使用界面来访问它们
    2. 不在继承之间共享,但在继承时设置为nil
    3. 更长时间写作
    4. 我还应该考虑什么?

4 个答案:

答案 0 :(得分: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

更多信息here

为了完整性:在两个呈现选项中,我更喜欢使用类实例变量,因为它通常是预期的行为。

答案 1 :(得分:3)

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

答案 2 :(得分:1)

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

class Foo
    @bar = 'baz'

    def Foo.print
        puts @bar
    end
end

总的来说,我会避免使用类变量,因为共享跨越继承模型通常非常令人惊讶且不明显;说实话,我不确定他们真正提供什么样的效用,除了不是全球化的全球。

如果你需要一个'scoped'全局变量,我会选择具有访问器的类级实例变量。你可以避免继承“惊喜”,同时仍然保持封装。

答案 3 :(得分: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_evaldefine_method等等,可能还有六种方法可以做到这一点,但现在应该让你满意。:-))

最后要注意的是:类实例变量只能通过它们定义的类来获得。如果你尝试从(或通过)子类调用任何这些方法,那么方法将会执行,但@变量都将为nil,因为self将是子类的类对象,而不是父类的类对象

class Bar < Foo
end

p [Bar.a, Bar.b, Bar.c, Bar.d]
#=> [nil, nil, nil, nil]