#{} 总是等于 to_s 吗?

Asa*_*uhi 5 ruby string-interpolation

在“使用类名进行字符串插值意味着什么? ”中,Candide 建议在#{}字符串内部隐式调用to_s. 因此,例如:

my_array = [1, 2, 3, 4]
p my_array.to_s # => "[1, 2, 3, 4]"
p "#{my_array}" # => "[1, 2, 3, 4]"
Run Code Online (Sandbox Code Playgroud)

但是,如果to_sfor Array 重新定义如下所示,我会得到不同的结果:

class Array
  def to_s
    self.map { |elem| elem.to_s }
  end
end

p my_array.to_s # => ["1", "2", "3", "4"]
p "#{my_array}" # => "#<Array:0x007f74924c2bc0>"
Run Code Online (Sandbox Code Playgroud)

我想这种情况随时都会发生,而且无论如何 to_s都会被覆盖。

如果可能的话,我应该做什么来保持字符串中的to_s表达式之间的相等性?#{}

我在RubyMonk 课程中遇到了这个问题:根据课程 #{ogres}应该返回什么,根据我的经验是不同的。

the*_*Man 2

看看 Ruby 告诉你的话:

"#{my_array}" # => "#<Array:0x007f74924c2bc0>"
Run Code Online (Sandbox Code Playgroud)

这意味着 Ruby 看到的是您的方法返回的数组to_s,而不是 Ruby 期望的字符串,如果您没有覆盖原始Array.to_s.

而是使用类似的东西:

'[%s]' % self.map { |elem| elem.to_s }.join(', ')
Run Code Online (Sandbox Code Playgroud)

更改您的代码以返回字符串,就可以了。

考虑一下:

[].class # => Array
[].to_s.class # => String

class Array
  def to_s
    self.map { |elem| elem.to_s }
  end
end

[].to_s.class # => Array

class Array
  def to_s
    '[%s]' % self.map { |elem| elem.to_s }.join(', ')
  end
end

[].to_s.class # => String

my_array = [1, 2, 3, 4]
"#{my_array}" # => "[1, 2, 3, 4]"
Run Code Online (Sandbox Code Playgroud)

在一般实践中,我建议谨慎地覆盖核心和 STD-Lib 类,to_s因为它们正在做它们应该做的事情。对于自定义类,最好实现to_s模仿与核心类相同的输出。有时,我们必须花点心思并提供更详细的视图来了解对象实例的外观,但那就是我们重写inspect.