7

两个模块Foo分别Baa定义了一个同名的方法,而name我在特定的上下文中做了。include Fooinclude Baa

当我调用时name,如何区分是否调用or的name方法?FooBaa

4

4 回答 4

1

只有模块包含的顺序决定了调用哪个模块。不能同时具有相同的名称 - 后者将覆盖前者。

当然,你可以做任何技巧,只是从我的头顶:

module A
  def foo
    :foo_from_A
  end
end

module B
  def foo
    :foo_from_B
  end
end

class C
  def initialize(from)
    @from = from
  end

  def foo
    from.instance_method(__method__).bind(self).call
  end

  private

  attr_reader :from
end

C.new(A).foo #=> :a_from_A
C.new(B).foo #=> :a_from_B

但这对现实生活中的用例没有好处:)

于 2018-09-24T18:26:04.310 回答
0

从技术上讲,没有名称冲突,因为方法 foo 被重新定义。

在下面的例子中,A.foo 被重新定义并且永远不会被调用

module A
  def foo
    raise "I'm never called"
  end
end

module B
  def foo
    puts :foo_from_B
  end
end

class C
  include A
  include B
end

C.new.foo
# =>
# foo_from_B

如果你写 A 和 B 模块,你可以使用 super 来调用之前定义的 foo。好像它在哪里继承了一个方法。

module A
  def foo
    puts :foo_from_A
  end
end

module B
  def foo
    super
    puts :foo_from_B
  end
end

class C
  include A
  include B
end

C.new.foo
# =>
# foo_from_A
# foo_from_B

有副作用,我不会使用它,但这样做可以解决问题:

module A
  def foo
    puts :foo_from_A
  end
end

module B
  def foo
    puts :foo_from_B
  end
end


class C
  def self.include_with_suffix(m, suffix)
    m.instance_methods.each do |method_name|
      define_method("#{method_name}#{suffix}", m.instance_method(method_name))
    end
  end
  include_with_suffix A, "_from_A"
  include_with_suffix B, "_from_B"
end

c= C.new
c.foo_from_A
c.foo_from_B
begin
  c.foo
rescue NoMethodError
  puts "foo is not defined"
end
# =>
# foo_from_A
# foo_from_B
# foo is not defined
于 2018-09-24T18:42:43.470 回答
0

如果没有任何方法FooBaa调用name(这似乎是一个合理的假设),则可以简单地创建别名。

module Foo
  def name; "Foo#name"; end
end

module Baa
  def name; "Baa#name"; end
end

class C
  include Foo
  alias :foo_name :name
  include Baa
  alias :baa_name :name
  undef_method :name
end

c = C.new
c.foo_name
  #=> "Foo#name"
c.baa_name
  #=> "Baa#name"

C.instance_methods & [:foo_name, :baa_name, :name]
  #=> [:foo_name, :baa_name]

关键字在此处alias记录。也可以使用#alias_method方法。有关两者的比较,请参阅此博客

Module#undef_method不是绝对必要的。name这只是为了确保在调用 时引发异常。

于 2018-09-24T20:18:33.660 回答
0

您应该明确阅读有关方法查找的信息。

无论如何,我会这样做:

module Foo
  def name
    :foo
  end
end

module Bar
  def name
    :bar
  end
end

class MyClass
  include Foo
  include Bar

  def foo_name
    Foo.instance_method(:name).bind(self).call
  end

  def bar_name
    Bar.instance_method(:name).bind(self).call
  end

  #
  # or even like this: obj.name(Foo)
  #

  def name(mod)
    mod.instance_method(:name).bind(self).call
  end
end

顺便说一句,如果您正在使用Module#instance_method并且UnboundMethod#bind您实际上并不需要包含特定模块。此代码有效:

Foo.instance_method(:name).bind('any object (e.g. string)').call
于 2018-09-25T10:03:39.850 回答