通过引用传递或通过副本传递 - Ruby Modules

时间:2013-11-09 12:01:59

标签: ruby

我不确定将对象传递给模块方法时会发生什么。对象是通过引用还是通过副本传递的?就像在这个例子中一样:

module SampleModule
  def self.testing(o)
    o.test
  end
end

class SampleClass
  def initialize(a)
    @a = a
  end

  def test
    @a = @a + 1
  end
end

sample_object = SampleClass.new(2)
3.times do
  SampleModule.testing(sample_object)
end
p sample_object # => #<SampleClass:somehexvalue @a=5>

似乎是传递参考。真的不清楚这个。

3 个答案:

答案 0 :(得分:2)

Ruby中的所有变量都是对象的引用。您不能以“通过引用传递”而不是“通过引用传递”,就像在C,C ++或Perl中使用该选项一样。 Ruby实际上强制传递值,否则没有其他选择。但是,发送的值始终是对象的引用。这有点像使用C或C ++,其中所有成员变量都是指针,或使用Perl,您必须始终使用引用,即使使用简单的标量。

我认为正是这种变量与对象数据的分离让你感到困惑。

几点:

  • 变量分配从不覆盖可能指向同一对象的其他变量。这几乎是pass-by-value的定义。但是,这不符合您对对象内容也受到保护的期望。

  • 实例变量和容器中的项目(例如ArrayString)是单独的变量,如果您发送容器,可以更改它的内容是直接的,因为您将引用发送到容器,并且包含其内容的相同的变量。我认为这就是你所说的“似乎是传递参考”

  • 某些类 - 包括代表数字的类和Symbol - 是不可变的,即数字4没有就地更改方法。但从概念上讲,您仍然将对单个对象4的引用传递到例程中(为了提高效率,Ruby将只在变量的内存分配中编码值4,但这是一个实现detail - 在这种情况下,值也是“指针”)。

  • 关闭到您使用SampleModule寻找的“按值传递”语义的最简单方法是clone例程的开始。请注意,这实际上并不会导致Ruby更改调用语义,只是在这种情况下,从方法的外部得到安全的假设(方法中的参数保留在方法内部),你似乎想要:


module SampleModule
  def self.testing(o)
    o = o.clone
    o.test
  end
end
  • 从技术上讲,这应该是一个通用的深度克隆,但不需要这样做才能使您的示例工作接近传值。您可以调用SampleModule.testing( any_var_or_expression )并知道代码的其余部分中的any_var_or_expression,相关对象将不会更改。

答案 1 :(得分:2)

如果你真的想要在词汇表上肛门,Ruby会通过值传递对(可变)对象的引用:

def pass_it(obj)
  obj = 'by reference'
end

def mutate_it(obj)
  obj << ' mutated'
end

str = 'by value'

pass_it(str)
mutate_it(str)

puts str # by value mutated

您可以使用dupclone(请注意两者都是浅色副本)和freeze来解决可能出现的问题。

答案 2 :(得分:-1)

Ruby中的所有内容都通过引用传递:

class Test
  attr_reader :a
  def initialize(a)
    @a = a
  end
end

s = "foo"
o = Test.new(s)
o.a << "bar"
o.a          #=> "foobar"
s            #=> "foobar"
o.a.equal? s #=> true

在您的代码中,您将对象传递给模块方法的事实并没有改变任何东西; sample_object已经是对新对象SampleClass.new(2)

的引用