协慌网

登录 贡献 社区

什么是 Ruby 中的 attr_accessor?

我很难理解 Ruby 中的attr_accessor 。谁可以给我解释一下这个?

答案

假设你有一个Person

class Person
end

person = Person.new
person.name # => no method error

显然我们从未定义方法name 。我们这样做。

class Person
  def name
    @name # simply returning an instance variable @name
  end
end

person = Person.new
person.name # => nil
person.name = "Dennis" # => no method error

啊哈,我们可以读取名称,但这并不意味着我们可以指定名称。这是两种不同的方法。前者称为读者 ,后者称为作家 。我们还没有创作作家,所以让我们这样做。

class Person
  def name
    @name
  end

  def name=(str)
    @name = str
  end
end

person = Person.new
person.name = 'Dennis'
person.name # => "Dennis"

真棒。现在我们可以使用 reader 和 writer 方法编写和读取实例变量@name 。除此之外,经常这样做,为什么每次浪费时间写这些方法呢?我们可以做得更轻松。

class Person
  attr_reader :name
  attr_writer :name
end

即使这样也会重复。当你想要读者和作家都只使用访问者!

class Person
  attr_accessor :name
end

person = Person.new
person.name = "Dennis"
person.name # => "Dennis"

以同样的方式工作!并猜测:我们的 person 对象中的实例变量@name将被设置,就像我们手动完成时一样,因此您可以在其他方法中使用它。

class Person
  attr_accessor :name

  def greeting
    "Hello #{@name}"
  end
end

person = Person.new
person.name = "Dennis"
person.greeting # => "Hello Dennis"

而已。为了理解attr_readerattr_writerattr_accessor方法如何为您实际生成方法,请阅读其他答案,书籍,ruby 文档。

attr_accessor 只是一种方法 。 (该链接应提供有关其工作原理的更多信息 - 查看生成的方法对,教程应向您展示如何使用它。)

诀窍是class 不是 Ruby 中的定义 (它只是 C ++ 和 Java 等语言中的 “定义”),但它是一个评估表达式 。在此评估期间,当调用attr_accessor方法时,该方法又修改当前类 - 记住隐式接收器: self.attr_accessor ,其中self是此时的 “open” 类对象。

attr_accessor和朋友的需求是:

  1. 与 Smalltalk 一样,Ruby 不允许在该对象的方法1之外访问实例变量。也就是说,实例变量不能以xy形式访问,就像在 Java 甚至 Python 中常见的那样。在 Ruby 中, y始终被视为要发送的消息(或 “要调用的方法”)。因此, attr_*方法创建了包装器,它通过动态创建的方法代理实例@variable访问。

  2. 锅炉很糟糕

希望这能澄清一些细节。快乐的编码。


1这不是严格正确的,并且围绕此一些 “技术” ,但没有对 “公共实例变量” 访问的语法支持。

attr_accessor (如 @pst 所述)只是一种方法。它的作用是为您创造更多方法。

所以这里的代码如下:

class Foo
  attr_accessor :bar
end

相当于这段代码:

class Foo
  def bar
    @bar
  end
  def bar=( new_value )
    @bar = new_value
  end
end

您可以在 Ruby 中自己编写这种方法:

class Module
  def var( method_name )
    inst_variable_name = "@#{method_name}".to_sym
    define_method method_name do
      instance_variable_get inst_variable_name
    end
    define_method "#{method_name}=" do |new_value|
      instance_variable_set inst_variable_name, new_value
    end
  end
end

class Foo
  var :bar
end

f = Foo.new
p f.bar     #=> nil
f.bar = 42
p f.bar     #=> 42