35

我刚开始学习 ruby​​,我看不出 an@instace_variable和使用声明的属性之间的区别attr_accessor

以下两个类有什么区别:

class MyClass  
  @variable1 
end

class MyClass
  attr_accessor :variable1
end

我在网上搜索了很多教程,每个人都使用不同的符号,它与 ruby​​ 版本有什么关系吗?我还在 StackOverflow 中搜索了一些旧线程

Ruby 中的 attr_accessor 是什么?
这两个 Ruby 类初始化定义有什么区别?

但我仍然无法弄清楚什么是最好的使用方式。

4

6 回答 6

58

实例变量在它所在的对象之外是不可见的;但是当您创建 时attr_accessor,它会创建一个实例变量,并使其在对象之外可见(和可编辑)。

带有实例变量的示例(不是attr_accessor

class MyClass
  def initialize
    @greeting = "hello"
  end
end

m = MyClass.new
m.greeting #results in the following error:
  #NoMethodError: undefined method `greeting' for #<MyClass:0x007f9e5109c058 @greeting="hello">

使用示例attr_accessor

class MyClass
  attr_accessor :greeting

  def initialize
    @greeting = "hello"
  end
end

m2 = MyClass.new
m2.greeting = "bonjour" # <-- set the @greeting variable from outside the object
m2.greeting #=> "bonjour"   <-- didn't blow up as attr_accessor makes the variable accessible from outside the object

希望这说明清楚。

于 2012-10-16T22:26:02.603 回答
30

实例变量在类之外是不直接可见的。

class MyClass
  def initialize
    @message = "Hello"
  end
end

msg = MyClass.new
@message
#==> nil   # This @message belongs to the global object, not msg
msg.message
#==> NoMethodError: undefined method `message'
msg.@message
#==> SyntaxError: syntax error, unexpected tIVAR

现在,您总是可以这样做:

msg.instance_eval { @message }

或者像这样直接询问变量:

msg.instance_variable_get :@message

但这很尴尬,有点作弊。在别人的课堂上闲逛可能具有教育意义,但不应要求您的客户端代码这样做以获得可靠的结果。因此,如果您希望客户能够看到这些值,请不要让他们使用上述技术;相反,定义一个显式公开值的方法:

class MyClass
  def message 
    return @message
  end
end
msg.message
# ==> "Hello"

因为你经常想要这样做,Ruby 提供了一个快捷方式来使它更容易。下面的代码与上面的代码具有完全相同的结果:

class MyClass
  attr_reader :message
end

这不是一种新类型的变量。这只是定义方法的一种简写方式。您可以查看msg.methods并看到它现在有一个message方法。

现在,如果您想让外人不仅可以看到实例变量的值,而且还可以更改它,该怎么办?为此,您必须定义一个不同的分配方法,=名称中带有 a:

class MyClass
  def message=(new_value)
    @message = new_value
  end
end
msg.message = "Good-bye"
msg.message
# ==> "Good-bye"

请注意,赋值运算符在这里是半魔法的;即使 and 之间有空格msg.message=Ruby 仍然知道调用该message=方法。诸如此类的组合运算符+=也会触发对该方法的调用。

同样,这是一个常见的设计,所以 Ruby 也为它提供了一个快捷方式:

class MyClass
  attr_writer :message
end

现在,如果你单独使用attr_writer,你会得到一个可以修改但看不到的属性。有一些奇怪的用例是您想要的,但大多数情况下,如果您要让外人修改变量,您也希望他们能够读取它。不必同时声明 anattr_reader和 an attr_writer,您可以像这样同时声明两者:

class MyClass
  attr_accessor :message
end

同样,这只是定义让您从类外部获取实例变量的方法的快捷方式。

于 2012-10-16T22:40:46.577 回答
9

attr_accesor为您提供读取和写入实例变量的方法。实例变量被设计为对外界隐藏,因此为了与它们通信,我们应该有 attr _ibute访问器方法。

于 2012-10-16T22:37:35.847 回答
3

在 OOPS 中,我们有一个称为封装的概念,这意味着对象的内部表示通常隐藏在对象定义之外的视图之外。只有对象“本身”才能弄乱它自己的内部状态。外面的世界不能。

每个对象通常由其状态和行为定义,在 ruby​​ 中,实例变量称为对象的内部状态或状态,根据 OOPS,任何其他对象都不应访问该状态,因此我们遵守封装。

前任:class Foo def initialize(bar) @bar = bar end end

上面,我们定义了一个类 Foo 并且在初始化方法中我们初始化了一个实例变量(属性)或(属性)。当我们使用 new 方法创建一个新的 ruby​​ 对象时,它又在内部调用 initialize 方法,当方法运行时,@bar 实例变量被声明并初始化,并将保存为对象的状态。

每个实例变量都有自己的内部状态,并且对对象本身来说是唯一的,我们在类中定义的每个方法都会根据方法定义和用途改变对象的内部状态。这里的initialize方法做同样的事情,比如创建一个新的实例变量。

var object = Foo.new(1)
#<Foo:0x00000001910cc0 @bar=1>

在后台,ruby 创建了一个实例变量 (@bar =1) 并将值作为对象的状态存储在对象“对象”中。我们可以使用“instance_variables”方法检查它,并且该方法根据对象的当前状态返回一个包含对象所有实例变量的数组。

object.instance_variables
#[
     [0]: @bar
 ]

我们可以在上面看到“@bar”实例变量。它是在我们调用对象的初始化方法时创建的。默认情况下,这个“@bar”变量不应该是可见的(隐藏的),因此除了对象之外的其他人不能从内部看到它。但是,一个对象可能会弄乱它自己的内部状态,这意味着如果我们给它一种方法,它可以显示或更改值,这两个可以通过在类中创建一个新的实例方法来完成。

当我们想通过调用@bar 变量来查看它时,我们会得到一个错误,因为默认情况下我们看不到对象的状态。

show = object.bar
#NoMethodError: undefined method `bar' for #<Foo:0x00000001910cc0 @bar=1>
#from (irb):24
#from /home/.rvm/rubies/ruby-2.0.0-p648/bin/irb:12:in `<main>'

但是我们可以通过两种方法访问变量,这两种方法称为setter 和 getter方法,它们分别允许对象显示或更改其内部状态(实例变量/属性/属性)。

class Foo
  def bar
    @bar
  end

  def bar=(new_bar)
    @bar = new_bar
  end
end

我们已经定义了 getter(bar) 和 setter(bar=) 方法,我们可以任意命名它们,但里面的实例变量必须与我们想要显示或更改值的实例变量相同。setter 和 getter 在某种程度上违反了 OOPS 概念,但它们也是非常强大的方法。

当我们通过重新打开类并定义它们来定义这两个方法时,当我们用方法调用对象时,我们可以查看实例变量(此处为@foo)并更改其值。

object.bar
1

object.bar=2
2

object.bar
2

这里我们调用了 bar 方法(getter),它返回 @bar 的值,然后我们调用了 bar= 方法(setter),我们提供了一个 new_value 作为参数,它改变了实例变量(@bar)的值,我们可以通过调用 bar 方法再看一遍。

在 ruby​​ 中,我们有一个名为attr_accessor的方法,它结合了 setter 和 getter 方法,我们将它定义在类内部的方法定义之上。attr_* 方法是创建方法的快捷方式(setter 和 getter)

class Foo
  attr_accessor :bar
end

我们必须提供一个符号 (:bar) 作为 attr_accessor 方法的参数,该方法在内部创建 setter 和 getter 方法,方法名称作为提供的符号名称。

如果我们只需要一个 getter 方法,我们可以调用 attr_reader :bar 如果我们只需要一个 setter 方法,我们可以调用 attr_writer :bar

attr_accessor 创建 attr_writer 和 attr_reader 方法

我们可以为 attr_* 方法提供任意数量的实例变量,用逗号分隔

class Foo
  attr_writer :bar
  attr_reader :bar
  attr_accessor :bar, :baz
end
于 2018-03-22T06:53:36.350 回答
1

因为attr_accessor定义了方法,所以您可以从类外部调用它们。A@variable只能从类内部访问。

于 2014-07-31T22:24:23.397 回答
1

另一个更紧凑的答案(对于 Java 开发人员) attr_accessor :x创建了 getter 和 setter@x

class MyClassA
  attr_accessor :x
end

是相同的

class MyClassB
  def x=(value) #java's typical setX(..)
    @x=value
  end
  def x
    @x
  end
end
于 2019-05-15T18:10:30.047 回答