4

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

ClassA.instance_methods(false)

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

ClassA.instance_methods(false).each do |method_name|
  ClassB.method_define(method_name, [body here??])
end

有没有办法得到对应的方法体,如果有,这个方法能用吗?如果没有,有没有办法做到这一点?

4

4 回答 4

10

其他人已经告诉你要子类化。但是要回答您的字面问题,我们将涉及UnboundMethod对象:

class Object
  def kokot; 'kokot' end
end

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

3.kokot
#=> kokot

到目前为止,一切都很好。现在让我们重新定义kokot方法Numeric

class Numeric
  def kokot; 'pica' end
end

o.kokot
#=> kokot
3.kokot
#=> pica

但是,如果我们决定,新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

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

um.source_location
#=> ["(irb)", 2]

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

RubyVM::InstructionSequence.disassemble( um )
#=> ( program listing goes here )

无论如何,Ruby 字节码看起来并不是那么漂亮。回到您最初的需求,甚至不能#define_methodUnboundMethod#bind不能将方法绑定到不兼容的对象。这不能被重新定义之类的技巧所欺骗#kind_of?,必须在本机代码中欺骗 CLASS_OF() 函数......

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

于 2013-01-03T06:16:52.677 回答
3

看起来你可能想要的是混合:

取自http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_modules.html

module Debug
  def whoAmI?
    "#{self.type.name} (\##{self.id}): #{self.to_s}"
  end
end
class Phonograph
  include Debug
  # ...
end
class EightTrack
  include Debug
  # ...
end
ph = Phonograph.new("West End Blues")
et = EightTrack.new("Surrealistic Pillow")
ph.whoAmI?  »   "Phonograph (#537766170): West End Blues"
et.whoAmI?  »   "EightTrack (#537765860): Surrealistic Pillow"
于 2013-01-03T04:45:55.350 回答
1

In ruby 2.0 you can use modules. Matz explicitly forbade this behavior from classes.

But you can use instance_methods from modules.

ModuleA.instance_methods(false).each do |name|
  meth = ModuleA.instance_method(name)
  ClassB.send(:define_method, name, meth)
end

define_method is a private method, so that's why you use send here.

But why do this? Just include the module.

If you want to just apply behavior to an object you can unbind a method from any module and bind it to any object.

ModuleA.instance_method(:something).bind(some_object).call(args)

If this is what you want, take a look at casting, a gem that adds a convenience to doing delegation like this as well as adding methods to an object only for the life of a block.

于 2013-06-10T17:13:49.680 回答
0

在这种情况下,classB应该继承classA.

于 2013-01-03T04:55:45.523 回答