将方法从一个类分配给另一个类的实例

pac*_*hun 4 ruby metaprogramming

正如标题所示,我想将一个类上定义的所有实例方法分配给另一个类.我知道我能得到的,我想从复制方法的列表ClassA,以ClassB这样的:

ClassA.instance_methods(false)
Run Code Online (Sandbox Code Playgroud)

我想我可以ClassB像这样定义它们:

ClassA.instance_methods(false).each do |method_name|
  ClassB.method_define(method_name, [body here??])
end
Run Code Online (Sandbox Code Playgroud)

有没有办法获得相应的方法体,如果有,这个方法会起作用吗?如果没有,有没有办法做到这一点?

Bor*_*cky 9

其他人已经告诉你要继承.但是为了回答你的文字问题,我们将参与UnboundMethod对象:

class Object
  def kokot; 'kokot' end
end

o = Object.new
o.kokot
#=> kokot

3.kokot
#=> kokot
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.现在让我们重新定义kokot方法Numeric:

class Numeric
  def kokot; 'pica' end
end

o.kokot
#=> kokot
3.kokot
#=> pica
Run Code Online (Sandbox Code Playgroud)

但是,如果我们决定,新kokot方法对数字很有用,但只有复数才能继续使用旧kokot方法.我们可以这样做:

um = Object.instance_method :kokot
#=> #<UnboundMethod: Object#kokot>
Complex( 2, 3 ).kokot # gives the redefined kokot method
#=> pica
Complex.module_exec { define_method :kokot, um }
# Now we've just bound the old kokot to Complex
Complex( 2, 3 ).kokot
#=> kokot
Run Code Online (Sandbox Code Playgroud)

简而言之,有一种方法可以在相关类中"复制和粘贴"方法.要求目标是未绑定方法源的子类.方法#source_location显示文件和#kokot已定义的行:

um.source_location
#=> ["(irb)", 2]
Run Code Online (Sandbox Code Playgroud)

对于内置方法,#source_location返回nil.在Ruby 2.0中,RubyVM类有方法#disassemble:

RubyVM::InstructionSequence.disassemble( um )
#=> ( program listing goes here )
Run Code Online (Sandbox Code Playgroud)

在任何情况下,Ruby字节码都不是那么漂亮.让我们再回到原来的需求,甚至#define_methodUnboundMethod#bind可结合方法不兼容的对象.这不能被像重新定义这样的技巧所欺骗#kind_of?,人们必须在本机代码中欺骗CLASS_OF()函数......

从可用的宝石,Sourcify,RubyParserSorcerer是有趣的.(谢谢@Casper.)使用这些,理论上可以通过#eval-ling提取的方法源在不兼容的对象之间移植代码.很长的路要走,这种技术仍然无法实现可靠的方法转移,因为只要源在运行时不可用(例如自修改源),它就会失败.