在Ruby中method_missing陷阱
在Ruby中定义method_missing
方法时有什么要注意的吗? 我想知道是否有一些从inheritance,exception抛出,性能或其他任何不太明显的交互。
有一点比较明显:总是重新定义respond_to?
如果你重新定义method_missing
。 如果method_missing(:sym)
有效, respond_to?(:sym)
应该总是返回true。 有很多依靠这个的库。
后来:
一个例子:
# Wrap a Foo; don't expose the internal guts. # Pass any method that starts with 'a' on to the # Foo. class FooWrapper def initialize(foo) @foo = foo end def some_method_that_doesnt_start_with_a 'bar' end def a_method_that_does_start_with_a 'baz' end def respond_to?(sym, include_private = false) pass_sym_to_foo?(sym) || super(sym, include_private) end def method_missing(sym, *args, &block) return foo.call(sym, *args, &block) if pass_sym_to_foo?(sym) super(sym, *args, &block) end private def pass_sym_to_foo?(sym) sym.to_s =~ /^a/ && @foo.respond_to?(sym) end end class Foo def argh 'argh' end def blech 'blech' end end w = FooWrapper.new(Foo.new) w.respond_to?(:some_method_that_doesnt_start_with_a) # => true w.some_method_that_doesnt_start_with_a # => 'bar' w.respond_to?(:a_method_that_does_start_with_a) # => true w.a_method_that_does_start_with_a # => 'baz' w.respond_to?(:argh) # => true w.argh # => 'argh' w.respond_to?(:blech) # => false w.blech # NoMethodError w.respond_to?(:glem!) # => false w.glem! # NoMethodError w.respond_to?(:apples?) w.apples? # NoMethodError
如果您的方法缺失方法只是寻找某些方法的名称,不要忘记调用超级,如果你没有find你要找的东西,所以其他方法的失误可以做他们的事情。
如果您可以预测方法名称,则最好dynamic声明它们,而不是依赖method_missing,因为method_missing会导致性能损失。 例如,假设您想要扩展数据库句柄,以便能够使用以下语法访问数据库视图:
selected_view_rows = @dbh.viewname( :column => value, ... )
您可以提前确定数据库中的所有视图,然后遍历它们以在@dbh上创build“viewname”方法,而不是依赖于数据库句柄的method_missing并将方法名称分配给数据库作为视图的名称。
基于Pistos的观点 : method_missing
至less比我试过的所有Ruby实现的常规方法调用慢一个数量级。 他有权预测何时可以避免调用method_missing
。
如果你觉得冒险,请查看Ruby的鲜为人知的Delegator类。
另一个难题:
method_missing
在obj.call_method
和obj.send(:call_method)
之间的行为不同。 基本上,前者不包含所有私有和未定义的方法,而后者不会错过私有方法。
所以当有人通过send
调用你的私有方法时,你的method_missing
将永远不会陷入通话。