这有效(在 irb 中测试):
注意:这只改变str
- 并非所有 String 实例。请阅读下文以详细了解其为何有效
another_str = "please don't change me!"
str = "ha, try to change my to_s! hahaha!"
proc = Proc.new { "take that, Mr. str!" }
singleton_class = class << str; self; end
singleton_class.send(:define_method, :to_s) do
proc.call
end
puts str.to_s #=> "take that, Mr. str!"
puts another_str.to_s #=> "please don't change me!"
# What! We called String#define_method, right?
puts String #=> String
puts singleton_class #=> #<Class:#<String:0x3c788a0>>
# ... nope! singleton_class is *not* String
# Keep reading if you're curious :)
这是有效的,因为你正在打开 str单例类 http://www.contextualdevelopment.com/articles/2008/ruby-singleton并在那里定义一个方法。因为这个,以及对模块#define_method http://ruby-doc.org/core/classes/Module.html#M001654,具有一些人所说的“平面范围”,您可以访问如果使用则超出范围的变量def to_s; 'whatever'; end
.
您可能想在这里查看一些其他“元编程咒语”:
media.pragprog.com/titles/ppmetr/spells.pdf http://media.pragprog.com/titles/ppmetr/spells.pdf
为什么只是改变str
?
因为 Ruby 有一些有趣的技巧。在 Ruby 对象模型中,方法调用导致接收者不仅搜索它的类(而且it's祖先),而且它也是单例类(或者如 Matz 所说,它是特征类)。这个单例类允许您[重新]定义单个对象的方法。这些方法称为“单例方法”。在上面的示例中,我们就是这样做的 - 定义一个单例方法名称to_s
。它的功能与此相同:
def str.to_s
...
end
唯一的区别是我们在调用时使用闭包Module#define_method
, 然而def
是一个关键字,它会导致范围的变化。
为什么不能简单一点呢?
好消息是您正在使用 Ruby 进行编程,所以请随意疯狂:
class Object
def define_method(name, &block)
singleton = class << self; self; end
singleton.send(:define_method, name) { |*args| block.call(*args) }
end
end
str = 'test'
str.define_method(:to_s) { "hello" }
str.define_method(:bark) { "woof!" }
str.define_method(:yell) { "AAAH!" }
puts str.to_s #=> hello
puts str.bark #=> woof!
puts str.yell #=> AAAH!
而且,如果你好奇……
你知道类方法吗?或者,在某些语言中,我们将它们称为静态方法?嗯,那些没有really存在于 Ruby 中。在 Ruby 中,类方法实际上只是 Class 对象的单例类中定义的方法。
如果这一切听起来很疯狂,请查看我上面提供的链接。只有知道如何进行元编程,才能充分利用 Ruby 的强大功能 - 在这种情况下,您确实想了解单例类/方法,更一般地说,了解 Ruby 对象模型。
HTH
-Charles