奇怪的行为:哈希的键取消动态方法定义

时间:2009-06-30 20:06:20

标签: ruby hash metaprogramming

假设我想要一些String的实例与其他“普通”实例的行为不同 - 例如取消“upcase”方法的效果。我做了以下事情:

class String
  def foo
    def self.upcase
      self
    end
    self
  end
end

似乎工作正常,我需要的方式:

puts "bar".upcase #=> "BAR"
puts "bar".foo.upcase #=> "bar"

但是,只要我使用String的欺骗实例作为Hash的键,行为开始对我来说很奇怪:

puts ({"bar".foo => "code"}).keys.first.upcase #=> "BAR", not "bar"!

...就好像忽略了foo方法,并将String的原始实例用作键。

任何人都可以看到这里发生了什么?非常感谢!

3 个答案:

答案 0 :(得分:5)

Ruby的Hash有一个使用字符串作为哈希键的特殊情况 - 它是字符串的内部副本。

基本上,它是为了保护您不使用字符串(对象)作为键,然后在代码中稍后更改该字符串对象,这可能会导致一些令人困惑的情况。可变键变得棘手。

不是将方法修改为返回更改的字符串类的字符串,而是创建一个新的字符串子类,它会覆盖upcase然后只设置它的值。

答案 1 :(得分:0)

在Ruby中扩展单个对象的常用方法是:

s = "bar"
class<<s
  def self.upcase
    self
  end
end

...但这并不能解决您的问题。似乎Ruby对作为字符串的哈希键或字符串的子类具有特殊规则,而不是字符串,您可以使用具有有意义的to_s定义的对象吗?

答案 2 :(得分:0)

因为在Ruby中你可以重新打开核心类并虚拟地重新定义所有内容,但这并不意味着你应该这样做。

强大的权力带来了巨大的责任,你的责任就是不再重新定义核心库方法,因为有几个对象可能需要它。 如果您的实例行为不像Sting,则声明您自己的类并扩展String。