如果在Ruby中一切都是对象,为什么这不起作用?

考虑到在Ruby编程语言中,所有东西都被称为Object,我安全地假设将参数传递给方法是通过引用来完成 。 然而,下面这个小例子让我困惑:

$string = "String" def changer(s) s = 1 end changer($string) puts $string.class String => nil 

正如您所看到的,原始对象未被修改,我想知道为什么 ,以及如何实现所需的行为,即。 获取方法实际更改其参数引用的对象。

Ruby的工作方式是传递值和传递引用的组合。 实际上,Ruby使用带引用的pass by值。

您可以在以下主题中阅读更多内容:

  • 通过引用传递或通过值传递
  • 通过参考传递?

一些值得注意的引用:

绝对正确:Ruby使用pass-value – 带引用。

 irb(main):004:0> def foo(x) x = 10 end => nil irb(main):005:0> def bar; x = 20; foo(x); x end => nil irb(main):006:0> bar => 20 irb(main):007:0> 

没有标准的方法(即除了涉及eval和元编程魔法之外)使调用范围中的变量指向另一个对象。 而且,顺便说一句,这与变量引用的对象无关。 Ruby中的直接对象与其他对象无缝集成(例如,不同于Java中的POD),从Ruby语言的角度来看,您看不出任何差异(除了性能之外)。 这是Ruby如此优雅的原因之一。

将参数传递给方法时,您传递的是指向引用的变量。 在某种程度上,它是传递值和传递参考的组合。 我的意思是,您将变量的值传递给方法,但变量的值始终是对象的引用。

和…之间的不同:

 def my_method( a ) a.gsub!( /foo/, 'ruby' ) end str = 'foo is awesome' my_method( str ) #=> 'ruby is awesome' str #=> 'ruby is awesome' 

和:

 def your_method( a ) a = a.gsub( /foo/, 'ruby' ) end str = 'foo is awesome' my_method( str ) #=> 'ruby is awesome' str #=> 'foo is awesome' 

就是在#my_method中,你正在调用#gsub! 它改变了对象(a)的位置。 由于’str’变量(在方法范围之外)和’a’变量(在方法范围内)都有一个“值”,它是对同一个对象的引用,对该对象的更改反映在’str’中’调用方法后的变量。 在#your_method中,调用#gsub,它不会修改原始对象。 相反,它会创建一个包含修改的String新实例。 将该对象分配给’a’变量时,您将’a’的值更改为对该新String实例的引用。 但是,’str’的值仍包含对原始(未修改)字符串对象的引用。

方法是更改​​引用还是引用的对象取决于类类型和方法实现。

 string = "hello" def changer(str) str = "hi" end changer(string) puts string # => "hello" 

string不会更改,因为string上的赋值将替换引用,而不是引用的值。 我想要修改字符串,你需要使用String#replace

 string = "hello" def changer(str) str.replace "hi" end changer(string) puts string # => "hi" 

字符串是一种常见情况,其中大部分操作适用于克隆,而不是自身实例。 出于这个原因,有几种方法具有在适当位置执行相同操作的爆炸版本。

 str1 = "hello" str2 = "hello" str1.gsub("h", "H") str2.gsub!("h", "H") puts str1 # => "hello" puts str2 # => "Hello" 

最后,要回答原始问题,您无法更改字符串。 您只能为其分配新值或将字符串包装到不同的可变对象中并替换内部引用。

 $wrapper = Struct.new(:string).new $wrapper.string = "String" def changer(w) w.string = 1 end changer($wrapper) puts $wrapper.string # => 1 

赋值不会将值绑定到对象,它会将对象引用绑定到标识符。 参数传递的工作方式相同。

当您输入函数体时,世界如下所示:

  +---+ +----------+ | s |----------------->| "String" | +---+ +----------+ ^ +-------+ | |$string|--------------------+ +-------+ 

代码

  s = 1 

让世界看起来像

  +---+ +---+ +----------+ | s |------>| 1 | | "String" | +---+ +---+ +----------+ ^ +-------+ | |$string|--------------------+ +-------+ 

赋值语法操纵变量,而不是对象。

像许多类似的语言(Java,C#,Python)一样,ruby是按值传递的,其中值通常是引用。

要操作字符串对象,可以在字符串上使用方法,例如s.upcase! 。 当它操纵对象本身时,这种事物将反映在方法之外。

因为$strings都是对同一对象的引用,所以字符串为“String”。 但是,当您将s指定为1 ,不会更改对象“String”,而是将其引用为新对象。

Ruby将值传递给函数,这些值是对象的引用。 在您的函数中,您将s重新分配给另一个值,在本例中是对1的引用。 它不会修改原始对象。

您的方法不是更改传入的对象,而是更改s引用的内容。

实际上,大多数托管编程语言如java,c#… almsot都不通过引用传递…

它们都通过值传递引用…这意味着它们会创建指向同一对象的另一个引用…为其赋值将不会更改原始引用的值…只是指向的内容。 ..

此外,字符串在大多数语言中是不可变的,这意味着你不能在创建后更改它们……它们必须重新创建为新的…所以你永远不会看到实际字符串的任何变化……