@instance_variable和attr_accessor之间的区别

时间:2012-10-16 21:56:41

标签: ruby

我刚刚开始学习ruby,我没有看到@instace_variable和使用attr_accessor声明的属性之间的区别。

以下两个类之间有什么区别:

class MyClass  
  @variable1 
end

class MyClass
  attr_accessor :variable1
end

我在网上搜索了很多教程,每个人都使用不同的符号,是否需要对ruby版本做任何事情?我还搜索了StackOverflow中的几个旧线程

  

What is attr_accessor in Ruby?
  What's the Difference Between These Two Ruby Class Initialization Definitions?

但我仍然无法弄清楚最佳使用方式。

6 个答案:

答案 0 :(得分:51)

实例变量在其所在的对象外部不可见;但是当你创建一个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

希望能说清楚。

答案 1 :(得分:27)

实例变量在课堂外不可见。

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 }

但这很尴尬和狡猾。在别人的课堂上学习可能很有教育意义,但如果你想获得可靠的结果,你的客户代码不应该这样做。另一方面,如果您希望客户能够看到这些值,请不要使用instance_eval;相反,定义一个实现技巧的方法:

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

因为您经常想要这样做,所以Ruby提供了一种快捷方式。下面的代码与上面的代码具有完全相同的结果:

class MyClass
  attr_reader :message
end

这不是一种新型变量;它只是定义方法的简便方法。您可以查看msg.methods,看看它现在有message方法。

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

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

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

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

class MyClass
  attr_writer :message
end

现在,如果您单独使用attr_writer,则会获得一个可以修改但未见的属性。有一些奇怪的用例,这就是你想要的,但大多数时候,如果你要让外人修改变量,你也希望它们能够读取它。您不必同时声明attr_readerattr_writer,而是可以同时声明两者:

class MyClass
  attr_accessor :message
end

同样,这只是一个快捷方式,用于定义允许您从类外部获取实例变量的方法。

答案 2 :(得分:8)

attr_accesor为您提供了读写实例变量的方法。实例变量被取消设置为对外界隐藏,因此为了与它们进行通信,我们应该有 attr _ibute accesor 方法。

答案 3 :(得分:2)

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

每个对象通常由其状态和行为定义,在ruby中,实例变量称为对象的内部状态或状态,并且根据OOPS,状态不应被任何其他对象访问,这样做我们坚持封装。

ex:class Foo def initialize(bar) @bar = bar end end

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

每个实例变量都有自己的内部状态,并且对象本身是唯一的,我们在类中定义的每个方法都将根据方法定义和目的改变对象的内部状态。这里的initialize方法也是如此,比如创建一个新的实例变量。

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

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

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

我们可以在上面看到'@bar'实例变量。当我们在对象上调用initialize方法时创建的。默认情况下,这个'@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和getters在某种程度上违反了OOPS概念,但它们也是非常强大的方法。

当我们通过重新打开类并定义它们来定义这两个方法时,当我们用方法调用对象时,我们就能够查看实例变量(这里是@foo)并改变它的值。 / p>

object.bar
1

object.bar=2
2

object.bar
2

这里我们调用了bar方法(getter),它返回@bar的值,然后我们调用了bar = method(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

答案 4 :(得分:1)

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

答案 5 :(得分:0)

另一个答案更为紧凑(适用于Java开发人员) attr_accessor :x创建@x的getter和setter方法

class MyClassA
  attr_accessor :x
end

相同
class MyClassB
  def x=(value) #java's typical setX(..)
    @x=value
  end
  def x
    @x
  end
end