当attr_accessor在类方法中时会发生什么?

Ash*_*ana 5 ruby attr-accessor

所以这出现在我的脑海里,想知道下面的一些事情发生时会发生什么.

 class Test 
   def self.abc
     attr_accessor :John 
   end
 end

 object = Test.new
 puts "before calling class method abc:  #{object.class.instance_methods(false)}"
 Test.abc
 puts "after calling class method abc:   #{object.class.instance_methods(false)}"
Run Code Online (Sandbox Code Playgroud)

在这里我检查的是,以这种方式创建getter和setter方法.如果是这样,那些实例方法或类方法.首先,我创建一个新对象,然后查看该对象的实例方法.在下一行之后,我运行该class方法abc然后再次检查实例的方法object.当时只有我能看到这两种方法JohnJohn=.这是怎么发生的?为什么运行类方法会动态地向已创建的对象添加方法?有人可以解释一下这个.

代码的输出是:

before calling class method abc:  []
after calling class method abc:   [:John, :John=]
Run Code Online (Sandbox Code Playgroud)

Chr*_*ald 7

在类方法中,self是类本身.因此,以下内容与他们最终做的相同:

class Test
  def self.abc
    # `self` in this context is Test.
    # This sends the message `:attr_accessor, :john` to `Test`
    attr_accessor :john
  end
end

class Test
  # `self` in this context is Test.
  # This sends the message `:attr_accessor, :john` to `Test`
  attr_accessor :john
end
Run Code Online (Sandbox Code Playgroud)

但是,正如您所指出的,Test::abc在解析类时不会执行,因此attr_accessor不会调用,并且不会添加实例方法.在运行时执行此操作是完全有效的,事实上,它是Rails中执行的大部分元编程的基础.

通常,如果您希望通过类方法添加访问器,则可以在定义之后调用该类方法,但仍然在类声明期间:

class Test
  def self.abc
    attr_accessor :john
  end

  abc
end
Run Code Online (Sandbox Code Playgroud)

这将实际运行,并在类上正确声明访问器!

至于你的问题:

为什么运行类方法会动态地向已创建的对象添加方法?

这是因为实例化类不会在实例化时实例化类的"快照" - 它会创建一个对象,该对象将其大部分功能(包括在其上发现实例方法)委托给与之关联的类.请注意,可以不扩展回类的实例上定义新方法:

class Test
  attr_accessor :foo
end

t1 = Test.new
t2 = Test.new

Test.send(:define_method, :bar) {}
puts t1.respond_to? :foo  # => true
puts t2.respond_to? :foo  # => true

puts t1.respond_to? :bar  # => true
puts t2.respond_to? :bar  # => true

t1.define_singleton_method(:baz) {}

puts t1.respond_to? :baz  # => true
puts t2.respond_to? :baz  # => false
Run Code Online (Sandbox Code Playgroud)