在 Ruby 中,我应该使用类变量还是类实例变量作为类静态变量?

发布于 2024-11-29 21:19:21 字数 608 浏览 1 评论 0原文

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. 写起来更长

还有什么我应该记住吗?

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

What I want to know is, which one should I use?
Which are the benefits and cons of each?

Class variables are:

  1. Private unless you make an interface
  2. Shared between inheritances
  3. Shorter to write

Class-instance variables are:

  1. Public, since you must use an interface to access them
  2. Not shared between inheritances, but are set to nil when inherited
  3. Longer to write

What else should I have in mind?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

怎言笑 2024-12-06 21:19:21

我最近发现 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

更多信息此处

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

I recently discovered ActiveSupport defines class_inheritable_accessor, which does what the class-instance variables do with the advantage that objects are not shared across inheritance, and you can have a default value for the variable when subclassing.

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

More info here

Just for completeness: of the two presented options, I prefer going with the class-instance variables, since is often the expected behavior.

幼儿园老大 2024-12-06 21:19:21

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

Never use class variables in Ruby, ever. They cause problems with inheritance. Always use class instance variables instead.

花开雨落又逢春i 2024-12-06 21:19:21

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

class Foo
    @bar = 'baz'

    def Foo.print
        puts @bar
    end
end

总体而言,我会避免使用类变量,因为跨继承共享模型通常非常令人惊讶且不明显;老实说,除了成为一个不太全球化的全球性之外,我不确定它们真正提供了什么实用性。

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

You also have the option of declaring instance variables at the class level:

class Foo
    @bar = 'baz'

    def Foo.print
        puts @bar
    end
end

Overall, I'd avoid class variables, as the shared-across-inheritance model is often very surprising and non-obvious; to be honest, I'm not sure what utility they really offer, other than being a not-quite-global-global.

If you need a 'scoped' global variable, I'd go for class-level instance variables with accessors. You avoid the inheritance 'surprise', while still maintaining encapsulation.

筱武穆 2024-12-06 21:19:21

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

现在我将在 StackOverflow 上的 Ruby 答案中阐明一些令人难以忍受的细节:

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

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

我们想要的是在类上定义方法,而不是在其实例上。这真正的意思是,这些方法位于类对象单例实例方法列表中。 (唷!)

所以,总结一下:在类对象 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 等,但现在应该让你满意:-))

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

class Bar < Foo
end

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

Class instance variables are generally more useful and less surprising than class variables, so you should probably use them.

And now I shall spell some things out in the excruciating detail you've come to expect from a Ruby answer on StackOverflow:

To declare or refer to a class instance variable, you need to be in class scope and use a single @ sign. This places the variable on the singleton class instance for that class.

Unfortunately, when you're in class scope and use the def keyword, your methods are placed on the list of instance methods for that class, and are executed in instance scope, so their @-sign variables will be on the instance they're in.

What we want instead is to define methods on the class, not on its instances. What that really means is that these methods are on the list of instance methods for the singleton class of the class object. (Phew!)

So, to sum up: There are at least four idioms for switching over and defining methods on the singleton class of the class object 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]

(There are probably half a dozen more ways to do this, once you factor in class_eval and define_method and the like, but that should satisfy you for now. :-))

One final note: class instance variables are only available via the class they're defined on. If you try to call any of those methods from (or via) a subclass, the methods will execute, but the @ variables will all be nil, since self will be the subclass's class object, not the parent class' class object.

class Bar < Foo
end

p [Bar.a, Bar.b, Bar.c, Bar.d]
#=> [nil, nil, nil, nil]
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文