类变量上的attr_accessor

77

attr_accessor 在以下代码中无法工作。错误提示为 "undefined method 'things' for Parent:Class (NoMethodError)":

class Parent
  @@things = []
  attr_accessor :things
end
Parent.things << :car

p Parent.things

然而下面的代码可以正常工作

class Parent
  @@things = []
  def self.things
    @@things
  end
  def things
    @@things
  end
end
Parent.things << :car

p Parent.things

5
attr_accessor是Ruby中用于在对象实例(以及实例变量)上创建setter和getter的简写方式,不适用于类级别变量。 - bjhaid
3
虽然我几乎不了解Rails,但我相信通过require 'active_support',你可以使用cattr_accessor :things - Cary Swoveland
相关链接:https://dev59.com/BHNA5IYBdhLWcg3wmfO5 - Ciro Santilli OurBigBook.com
8个回答

118

attr_accessor定义了一个实例的访问器方法。如果您想要类级别自动生成的访问器,您可以在元类上使用它。

class Parent
  @things = []

  class << self
    attr_accessor :things
  end
end

Parent.things #=> []
Parent.things << :car
Parent.things #=> [:car]

但请注意,这将创建一个类级实例变量,而不是一个类变量。无论如何,这很可能是您想要的,因为当处理继承时,类变量的行为与您预期的不同。请参阅“Ruby中的类变量和实例变量”。


这仍然返回相同的错误。class Parent @@things = [] class << self attr_accessor :things end end Parent.things << :carp Parent.things - Talespin_Kit
1
看我的编辑 - 我为你添加了初始化。由于它是类级实例变量而不是类变量,所以你只需要使用一个@ - Alex.Bullard
如果我只想让@things在类内部可用怎么办?对于实例变量,我可以使用attr_reader,但是对于类级别的实例变量,我必须依靠attr_accessor自己编写(假设它不是数组,而是像整数这样的不可变对象)。 - Franklin Yu
“一个类级别的实例变量而不是类变量” - 我感觉一半的答案告诉你要做一种,另一半告诉你要做另一种。但仍然没有人说出它们之间的区别以及如何选择。 - Nakilon

19

attr_accessor 会为实例变量生成访问器。在 Ruby 中,类变量是非常不同的东西,它们通常不是你想要的。你可能想要的是类实例变量。你可以这样使用 attr_accessor 来处理类实例变量:

class Something
  class << self
    attr_accessor :things
  end
end

然后你可以写Something.things = 12,它就可以工作。


在这种情况下,如果一个类继承了Something,那么继承类的实例对象会得到一个新的things副本,对吗? - Talespin_Kit
@Talespin_Kit:对的 - 每个类都会有自己的@things变量,因为实例变量是特定于拥有它们的对象的。 - Chuck

5
这可能是最简单的方法。
class Parent
  def self.things
    @@things ||= []
  end
end
Parent.things << :car

p Parent.things

5

需要澄清的是:使用attr_accessor无法访问类变量,它只与实例变量有关:

class SomeClass
  class << self
    attr_accessor :things
  end
  @things = []
end

因为在Ruby中,类是"Class"类的实例(天啊,我喜欢说这个),而attr_accessor为实例变量设置访问器方法。


1
请注意,单例方法是仅适用于单个对象的方法。在Ruby中,类也是一个对象,因此它也可以具有单例方法!因此,在调用它们时要注意。
示例:
class SomeClass
  class << self
    def test
    end
  end
end

test_obj = SomeClass.new

def test_obj.test_2
end

class << test_obj
  def test_3
  end
end

puts "Singleton methods of SomeClass"
puts SomeClass.singleton_methods
puts '------------------------------------------'
puts "Singleton methods of test_obj"
puts test_obj.singleton_methods

SomeClass的单例方法

test


单例方法的 test_obj
test_2
test_3

1
class Parent
  @things = []
  
  singleton_class.send(:attr_accessor, :things)
end

当您动态定义访问器或在方法内部创建访问器时,此模式最为有用:

class Foo
  def self.add_accessor(name)
    singleton_class.send(:attr_accessor, name)
  end
end

Foo.add_accessor :things
Foo.things = [:car]
Foo.things # => [:car]

1
Parent.class_variable_get(:@@things)

这将是内置的方式。在大多数情况下,我认为这应该足够了。不需要在实例中拥有类变量访问器。


0

我认为你想要的是 class_attribute

class Base
  class_attribute :foo
  self.foo = '--UNSET--'

  def show
    puts self.foo
  end
end

class X < Base
  self.foo = 'XXX'
end

class Y < Base
  self.foo = 'YYY'
end

class Z < Base
end

puts X.foo # -> 'XXX'
puts Y.foo # -> 'YYY'
puts Z.foo # -> '--UNSET--'
X.new.show # -> 'XXX'
X.foo = 'x-x-x'
X.new.show # -> 'x-x-x'
Y.new.show # -> 'YYY'

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接