【问题标题】:Assign a method from one class to an instance of another将一个类的方法分配给另一个类的实例
【发布时间】:2013-01-03 03:49:14
【问题描述】:

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

ClassA.instance_methods(false)

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

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

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

【问题讨论】:

  • 为什么要这样做?你有什么理由不能使用 mix-ins /modules 来完成这个任务?
  • @John:我不认为 OP 是不懂子类化概念的菜鸟。你为什么不从字面上理解他的问题呢?
  • 您的代码与您的标题相矛盾。您希望 ClassA 和 ClassB(不是它的单个实例)共享相同的方法。 John Naegle 的答案是正确的:将 ClassA 的所有实例方法移动到一个模块中,然后将此模块包含到两个类中。

标签: ruby metaprogramming


【解决方案1】:

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

class Object
  def kokot; 'kokot' end
end

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

3.kokot
#=> kokot

到目前为止一切顺利。现在让我们在Numeric 上重新定义kokot 方法:

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_method 或UnboundMethod#bind 也不能将方法绑定到不兼容的对象。这不能被重新定义#kind_of? 之类的技巧所欺骗,必须在本机代码中欺骗 CLASS_OF() 函数......

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

【讨论】:

  • 我不使用 Ruby,而是使用 dobrá odpoveď! :D
【解决方案2】:

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

取自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"

【讨论】:

  • 在我看来是合适的答案:)
【解决方案3】:

在 ruby​​ 2.0 中,您可以使用模块。 Matz 明确地forbade this behavior from classes。

但您可以使用模块中的 instance_methods。

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

define_method 是一个私有方法,这就是你在这里使用send 的原因。

但是为什么要这样做呢?只需包含模块即可。

如果您只想将行为应用于对象,您可以从任何模块取消绑定方法并将其绑定到任何对象。

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

如果这是您想要的,请查看casting,类似这样的gem that adds a convenience to doing delegation,以及仅为块的生命周期向对象添加方法。

【讨论】:

    【解决方案4】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-09-17
      • 1970-01-01
      • 1970-01-01
      • 2023-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多