让class Example
定义为:
class Example
def initialize(test='hey')
self.class.send(:define_method, :say_hello, lambda { test })
end
end
在致电Example.new; Example.new
时,我得到warning: method redefined; discarding old say_hello
。我总结说,这必须是因为它在实际的类中定义了一个方法(从语法上来说这是有意义的)。当然,如果Example
的多个实例在其方法中具有不同的值,那么这将是灾难性的。
有没有办法只为该实例内部的类实例创建方法?
答案 0 :(得分:69)
您需要获取对实例的单例类的引用,该类包含所有特定于实例的内容,并在其上定义方法。在红宝石1.8中,它看起来有点凌乱。 (如果您找到更清洁的解决方案,请告诉我!)
Ruby 1.8
class Example
def initialize(test='hey')
singleton = class << self; self end
singleton.send :define_method, :say_hello, lambda { test }
end
end
然而,Ruby 1.9提供了一种更简单的方法。
Ruby 1.9
class Example
def initialize(test='hey')
define_singleton_method :say_hello, lambda { test }
end
end
答案 1 :(得分:39)
首先,小样式提示:
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
的{{1}} 1}}然后将其转换回块:
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 方法,这个方法的目的是定义特定于一个对象。但是您要为类的每个实例定义单例方法,并且为类的每个实例定义相同的单例方法。我们已经有了一种机制来定义类的每个实例的行为:实例方法。
这两个论点来自完全相反的方向,但它们到达同一目的地:
class Example
def initialize(test='hey')
@test = test
end
def say_hello
@test
end
end
答案 2 :(得分:8)
我知道这是两年前被问到的,但我想补充一个答案。 string = "String"
string.instance_eval do
def new_method
self.reverse
end
end
将有助于将方法添加到实例对象
{{1}}