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

What happens when attr_accessor is within a class method?

所以我想到了这个,想知道当下面的事情完成后会发生什么。

 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)}"

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

代码的输出是:

before calling class method abc:  []
after calling class method abc:   [:John, :John=]

在 class 方法中,self 是 class 本身。因此,以下内容在他们最终所做的事情上是等价的:

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

但是,正如您所指出的,解析 class 时不会执行 Test::abc,因此不会调用 attr_accessor,也不会添加实例方法。在 运行 时执行此操作是完全有效的,事实上,这是在 Rails.

中执行的大部分元编程的基础

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

class Test
  def self.abc
    attr_accessor :john
  end

  abc
end

这实际上 运行,并在 class!

上正确声明访问器

关于您的问题:

How come a running of a class method dynamically add methods to already created objects?

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

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

首先,请注意代替

object = Test.new
object.class.instance_methods(false)

你可以简单地写

Test.instance_methods(false)

所以让我们简化一下:

puts "before calling class method abc:  #{Test.instance_methods(false)}"
  # (prints) []
Test.abc
puts "after calling class method abc:   #{Test.instance_methods(false)}"
  # (prints) [:John, :John=]

对此行为有一个简短的解释,但也有一个较长的解释。

简短说明

Test.method(:attr_accessor)
  #=> #<Method: Class(Module)#attr_accessor> 

这个return值,连同

这个事实
Test.class #=> Class

告诉我们 attr_accessorClass 的 instance_method,因此是 Test 的方法。让我们确认一下。

Class.instance_methods.include?(:attr_accessor)
  #=> false

糟糕! (你期待 => true?)。那只能意味着 attr_accessor 是受保护的或私有的实例方法:

Class.protected_instance_methods.include?(:attr_accessor)
  #=> false

Class.private_instance_methods.include?(:attr_accessor)
  #=> true

因此

Class.private_instance_methods.include?(:attr_accessor)
  #=> true

所以attr_accessor只是Test的class、Class的私有实例方法,使其成为Test的私有方法。表达式(来自之前的)

Test.method(:attr_accessor)
  #=> #<Method: Class(Module)#attr_accessor> 

也告诉我们attr_accessor定义在Class的superclass Module.

Class.superclass
  #=> Module
Class.ancestors
  #=> [Class, Module, Object, Kernel, BasicObject] 
Class.instance_method(:attr_accessor).owner
  #=> Module 
Test.method(:attr_accessor).owner
  #=> Module 

更长的解释

方法 Module#attr_accessor 可以通过以下两种方式之一提供给 class Test。这就是 Ruby 的 对象模型 的本质。 (我回避了单例 class 方法的位置。)

1. Test从它的superclass

继承方法
Test.superclass
  #=> Object
Test.ancestors
  #=> [Test, Object, Kernel, BasicObject]

让我们看看。

Object.method(:attr_accessor)
  #=> #<Method: Class(Module)#attr_accessor> 
Object.public_methods.include?(:attr_accessor)
  #=> false 
Object.private_methods.include?(:attr_accessor)
  #=> true

2。 attr_accessorTest的class

的实例方法
Test.class
  #=> Class

Class.private_instance_methods.include?(:attr_accessor)
  #=> true

所以Test既从它的superclass继承了attr_accessor,又可以作为它的class的一个实例方法。这种二元性通常用 this one.

这样的图表来解释。

Ruby 首先检查 Test 的 class、Class 的实例方法 attr_accessor。它会在哪里找到它。 (如果她没有在那里找到它,那么她会在 superclass 中寻找方法(不是实例方法)。)