首先,一个小的风格提示:
self.class.send(:define_method, :say_hello, lambda { test })
您可以使用 Ruby 1.9 中的新 proc 字面量使它看起来更漂亮:
self.class.send(:define_method, :say_hello, -> { test })
但你不需要那个。 Ruby 有一个叫做 blocks 的东西,它基本上是一段代码,你可以将它作为参数传递给一个方法。事实上,您已经使用了块,因为lambda 只是一个将块作为参数并返回Proc 的方法。但是,define_method 已经 接受了一个块,无需将块传递给 lambda 将其转换为 Proc 并传递给 define_method 然后将其转换回成一个块:
self.class.send(:define_method, :say_hello) { test }
正如您已经注意到的,您在错误的类上定义了方法。您在Example 类上定义它,因为在initialize、self 等实例方法内部是当前对象(即@mikej 示例中的ex1 或ex2),这意味着self.class 是ex1 的班级,即Example。所以,你一遍又一遍地覆盖同一个方法。
这会导致以下不良行为:
ex1 = Example.new('ex1')
ex2 = Example.new('ex2') # warning: method redefined; discarding old say_hello
ex1.say_hello # => ex2 # Huh?!?
相反,如果你想要一个单例方法,你需要在单例类上定义它:
(class << self; self end).send(:define_method, :say_hello) { test }
这按预期工作:
ex1 = Example.new('ex1')
ex2 = Example.new('ex2')
ex1.say_hello # => ex1
ex2.say_hello # => ex2
在 Ruby 1.9 中,有一个方法可以做到这一点:
define_singleton_method(:say_hello) { test }
现在,它按照您希望的方式工作,但这里有一个更高级别的问题:这不是 Ruby 代码。它是 Ruby 语法,但它不是 Ruby 代码,而是 Scheme。
现在,Scheme 是一门出色的语言,用 Ruby 语法编写 Scheme 代码当然不是一件坏事。它打败了用 Ruby 语法编写 Java 或 PHP 代码,或者就像昨天 StackOverflow 问题中的情况一样,用 Ruby 语法编写 Fortran-57 代码。但它不如用 Ruby 语法编写 Ruby 代码。
Scheme 是一种函数式语言。函数式语言使用函数(更准确地说,函数闭包)进行封装和状态。但 Ruby 不是函数式语言,它是面向对象的语言,OO 语言使用 objects 进行封装和状态。
因此,函数闭包变成了对象,捕获的变量变成了实例变量。
我们也可以从一个完全不同的角度来解决这个问题:您正在做的是定义一个 singleton 方法,该方法的目的是定义特定于一个对象。但是您正在为类的 每个 实例定义该单例方法,并且您正在为该类的每个实例定义 same 单例方法。我们已经有了一种为类的每个实例定义行为的机制:实例方法。
这两个论点来自完全相反的方向,但它们到达了同一个目的地:
class Example
def initialize(test='hey')
@test = test
end
def say_hello
@test
end
end