让我们class Example
定义为:
class Example
def initialize(test='hey')
self.class.send(:define_method, :say_hello, lambda { test })
end
end
Run Code Online (Sandbox Code Playgroud)
在打电话给Example.new; Example.new
我得到一个warning: method redefined; discarding old say_hello
.我总结说,这必须是因为它在实际的类中定义了一个方法(从语法上来说这是有意义的).当然,如果Example
在他们的方法中存在多个具有不同值的实例,那将是灾难性的.
有没有办法从该实例内部为类的实例创建方法?
tho*_*ncp 72
您需要获取对实例的单例类的引用,该类包含所有特定于实例的内容,并在其上定义方法.在红宝石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
Run Code Online (Sandbox Code Playgroud)
但是,Ruby 1.9提供了一种更简单的方法.
Ruby 1.9
class Example
def initialize(test='hey')
define_singleton_method :say_hello, lambda { test }
end
end
Run Code Online (Sandbox Code Playgroud)
Jör*_*tag 39
首先,一个小风格的提示:
self.class.send(:define_method, :say_hello, lambda { test })
Run Code Online (Sandbox Code Playgroud)
你可以通过在Ruby 1.9中使用新的proc文字来使这个看起来更好一些:
self.class.send(:define_method, :say_hello, -> { test })
Run Code Online (Sandbox Code Playgroud)
但你不需要那样做.Ruby有一些称为块的东西,它们基本上是一段代码,可以作为参数传递给方法.实际上,您已经使用了块,因为lambda
它只是一个将块作为参数并返回一个的方法Proc
.但是,无论如何define_method
已经占用了一个块,没有必要传递一个块,lambda
它将它转换为Proc
它传递给define_method
它的块然后将它转换回块:
self.class.send(:define_method, :say_hello) { test }
Run Code Online (Sandbox Code Playgroud)
正如您已经注意到的那样,您正在错误的类上定义方法.正在定义它的Example
类,由于内侧的实例方法一样initialize
,self
是当前的对象(即,ex1
或ex2
在@ mikej的示例),这意味着self.class
是ex1
的类,这是Example
.所以,你一遍又一遍地覆盖相同的方法.
这会导致以下不必要的行为:
ex1 = Example.new('ex1')
ex2 = Example.new('ex2') # warning: method redefined; discarding old say_hello
ex1.say_hello # => ex2 # Huh?!?
Run Code Online (Sandbox Code Playgroud)
相反,如果你想要一个单例方法,你需要在单例类上定义它:
(class << self; self end).send(:define_method, :say_hello) { test }
Run Code Online (Sandbox Code Playgroud)
这按预期工作:
ex1 = Example.new('ex1')
ex2 = Example.new('ex2')
ex1.say_hello # => ex1
ex2.say_hello # => ex2
Run Code Online (Sandbox Code Playgroud)
在Ruby 1.9中,有一种方法可以做到这一点:
define_singleton_method(:say_hello) { test }
Run Code Online (Sandbox Code Playgroud)
现在,这可以按照你想要的方式工作,但这里有一个更高级别的问题:这不是Ruby代码.它是Ruby 语法,但它不是Ruby代码,它是Scheme.
现在,Scheme是一种出色的语言,用Ruby语法编写Scheme代码肯定不是坏事.它胜过用Ruby语法编写Java或PHP代码,或者像昨天的StackOverflow问题中的情况一样,使用Ruby语法中的Fortran-57代码.但它不如在Ruby语法中编写Ruby代码那么好.
Scheme是一种功能语言.函数式语言使用函数(更准确地说,函数闭包)来进行封装和状态.但Ruby不是一种功能语言,它是面向对象的语言,OO语言使用对象进行封装和状态.
因此,函数闭包成为对象,捕获的变量成为实例变量.
我们也可以从一个完全不同的角度来看待你:你正在做的是你正在定义一个单例方法,这个方法的目的是定义一个特定于一个对象的行为.但是,您要为该类的每个实例定义该单例方法,并为该类的每个实例定义相同的单例方法.我们已经有了一种机制来定义类的每个实例的行为:实例方法.
这两个论点来自完全相反的方向,但它们到达同一目的地:
class Example
def initialize(test='hey')
@test = test
end
def say_hello
@test
end
end
Run Code Online (Sandbox Code Playgroud)
我知道这是两年前被问到的,但我想补充一个答案..instance_eval
将有助于向实例对象添加方法
string = "String"
string.instance_eval do
def new_method
self.reverse
end
end
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
28236 次 |
最近记录: |