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

Should I use class variables or class-instance variables for class static variables in Ruby?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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

我想知道的是,我应该使用哪一个?每种方法的好处和缺点是什么?

类变量是:

  • 私有,除非您创建一个接口
  • 在继承人之间共享
  • 写得更短
  • 类实例变量是:

  • 公共的,因为您必须使用接口来访问它们
  • 不在继承之间共享,但在继承时设置为零
  • 写的时间更长
  • 我还应该考虑什么?


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


    我最近发现activesupport定义了class_inheritable_accessor,它利用类实例变量的优点,即对象不能在继承中共享,并且在子类化时可以为变量设置一个默认值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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

    更多信息在这里

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


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

    现在,我将在stackoverflow上的一个Ruby答案中详细说明一些您所期望的难以忍受的细节:

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

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

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

    综上所述,在类对象Foo的单例类上,至少有四种转换和定义方法的习惯用法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    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之类的因素,也许还有6种方法可以做到这一点,但现在应该能让你满意了。-)

    最后一个注意事项:类实例变量只能通过在其上定义的类来使用。如果试图从子类(或通过子类)调用这些方法中的任何一个,这些方法将执行,但@variables将全部为零,因为self将是子类的类对象,而不是父类的类对象。

    1
    2
    3
    4
    5
    class Bar < Foo
    end

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

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

    1
    2
    3
    4
    5
    6
    7
    class Foo
        @bar = 'baz'

        def Foo.print
            puts @bar
        end
    end

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

    如果您需要一个"作用域"全局变量,我将使用访问器来查找类级实例变量。您可以在保持封装的同时避免继承"意外"。