9

一般 Ruby 问题:在 Ruby 中,我经常看到在类中的代码,但不是方法的一部分。例如:

class DooDad
   attr_accessor :foo
end

或者

class Teacher < ActiveRecord::Base
   has_many :students
end

我认为attr_accessor和是分别用or参数has_many调用的方法,对吗?如果是这样,何时执行这些类型的语句。我试过这个::foo:students

class DooDad
  attr_accessor :foo
  puts "I happened!"
  @foo = 7
end

它似乎没有运行这些new方法的一部分:

dd = DooDad.new
dd.foo

输出 nil,从不吐出任何puts东西

这一切究竟是如何运作的?

4

3 回答 3

16

方法喜欢attr_accessor并且has_many通常被称为“模仿方法”,因为它们看起来有点像 ruby​​ 关键字(模仿它们),但实际上,正如您和其他人正确指出的那样,它们是方法调用。

dd = DooDad.new
dd.foo

输出 nil,从不吐出任何puts东西

这一切究竟是如何运作的?

当您在类定义中时,所有方法调用和“变量定义”的隐式接收器是self,在您的情况下是DooDad.

所以当你写作时

class DooDad
  @foo = 1
end

您实际上是在定义一个实例变量 on self,这恰好是类本身,因为您在该类定义中。(以及在任何其他类、模块或方法定义之外)

attr_accessor另一方面,在元编程的帮助下,该方法为从 class 实例化的对象的实例变量生成访问器方法DooDad

回到你的例子:

class DooDad
  attr_accessor :foo
  puts "I happened!"
  @foo = 7
end

有了上面提到的内容,您现在应该明白您正在处理两个不同的@foo 变量,一个用于 DooDad 类的实例(即DooDad.new),另一个(您通过编写创建的@foo = 7)用于 DooDad 类本身!

在类上调用new方法时,您会创建它的一个实例。

dd = DooDad.new 
#=> dd is now an object of class DooDad

dd.foo 
#=> You just called the "getter" method for an instance variable foo of object dd, which was never defined before, that's why it's returning nil.

puts "I happened!"语句,实际上就像其他两个一样,在加载类时立即进行评估,但在您调用new它时不会。如果你想要你描述的行为(调用时做事new),我建议实现一个initialize()方法 for DooDad,当你调用时会调用它new

class DooDad
    attr_accessor :foo

    def initialize()
      puts "I happened!"
      @foo = 7
    end
end

dd = DooDad.new
#=> outputs "I happened!" and sets dd.foo = 7

dd.foo
#=> 7

但是为什么@foo = 7现在设置实例变量ddand notDooDad呢?当您使用关键字定义方法时def,您输入了一个新范围(您通过了范围门)。self现在不再是类,而是您使用创建的该类的实例,new就像dd. 因此,当您在@foo = 7方法定义内部编写时,您谈论的是类实例的变量DooDad,而不是类本身。

这篇文章可能太长了,甚至可能无法令人满意,但我希望它有点全面。

于 2012-08-21T21:38:06.973 回答
7

类似has_manyattr_accessor的方法实际上是模块或类上的方法。您完全正确地认为它们是普通方法,就像任何其他方法一样使用参数调用。当直接在类中调用方法时(在方法定义之外),它是在类本身上调用的。

这是attr_accessor的文档。

于 2012-08-21T21:31:27.780 回答
-2

它们是语言中内置的语法糖,因此与您的测试不同。当你写(例如)

class Foo
  attr_accessor :bar
end

它实际上是...的简写

class Foo
  def bar
    @bar
  end

  def bar=(value)
    @bar = value
  end
end
于 2012-08-21T21:20:44.373 回答