'的respond_to&#39?;与?定义?'

时间:2017-05-22 07:55:57

标签: ruby methods metaprogramming

如果我想检查是否定义了具有给定名称的方法,哪个更好用,respond_to?defined?

从效率的角度来看,可能存在使用defined?的论据,因为defined?是内置关键字,而respond_to?是一种方法,因此前者可能要快点但另一方面,在已知要检查的表达式是一种简单方法的情况下,defined?需要解析整个表达式,与使用respond_to?相比,这可能是一个缺点。 ,只需要接受参数作为方法名称。

哪个更好?是否应考虑效率以外的其他因素?

2 个答案:

答案 0 :(得分:7)

  

如果我想检查是否定义了具有给定名称的方法,哪个更好用,respond_to?defined?

都不是。使用Module#method_defined?

这不是一个真正的问题,而是更好的"使用:Object#respond_to?方法和defined?一元前缀运算符(尽管名称!)都不会检查方法是否定义:它们都是检查接收方是否响应 消息,这是完全不同的事情。

只有Module#method_defined?才会实际检查方法是否已定义:

class Foo
  def method_missing(*) end
  def respond_to_missing?(*) true end
end

foo = Foo.new

defined? foo.bar
#=> 'method'

foo.respond_to?(:bar)
#=> true

Foo.method_defined?(:bar)
#=> false

答案 1 :(得分:0)

我的简短回答是我要么说。但是,Module#method_defined?可能不适用于大多数情况。 这完全取决于您想知道什么。这是一个很长的答案,解释了目的。

我想最受欢迎的目的是Duck-typing, 即检查对象是否对具有特定名称的方法进行响应,如果响应,则不必理会对象实际上是什么类,或者不在内部实现方法的方式。在这种情况下,Object#respond_to? (我认为)是为此设计的确切方法(如方法名称所示)。的确(如@JörgW Mittag指出的),如果在其类中重新定义Object#respond_to_missing?,则结果可能会改变。但是,这就是respond_to_missing?的全部目的-将方法注册为有效-例如,请参见Ruby核心开发人员Marc-André的(旧)博客文章“ Method_missing, Politely”进行澄清。< / p>

另一种传统方法(自Ruby 1起)是使用Ruby内置的defined?This past answer改为“在Ruby中,如何检查方法“ foo =()”是否已定义?”解释了defined?特有的潜在有用案例,即告诉它是否为赋值。为了检查某个方法是否存在,在大多数(或所有?)情况下,defined?的工作方式与Object#respond_to?相似,只是它返回String对象或 nil 而不是布尔值。我会说这取决于个人喜好;但是,我在总体上赞成Object#respond_to?,因为它显然更具体并且易于阅读,并且实际键入的潜在麻烦(如忘记为defined?加上一对括号)的可能性较小。

或者,要检查对象的类是否具有在其自身之一,其超类和包含的模块之一中名称明确定义的方法,请使用Module#method_defined?。具体来说,此方法将忽略respond_to_missing?,这意味着实际上忽略了任何使用BasicObject#method_missing的元编程(例如ActiveRecord of Rails广泛使用的元编程)。

此外,从Ruby 2.6.5开始,此Module#method_defined?会忽略refinements,也就是说,using引入的模块方法被视为定义,与Object#respond_to?和内置defined?相比,两者都认为已定义了这些 refine-d 方法。

更原始的方法是使用obj.methods.include?(:bar)之类的Object#methods来检查公共方法和受保护方法。这种方式的优点是,您可以通过指定参数true来排除对象类所包含的模块中的方法,例如obj.methods(false).include?(:bar)。另外,如果需要,您可以使用Object#private_methodsObject#protected_methodsObject#public_method进行更好的区分。它们都考虑了Object#respond_to_missing?

另一种注释私有方法,包括内置的功能(也称为Kernel方法)。 Object#respond_to?默认情况下不会响应私有方法,除非第二个参数的值为true(请参阅上一个问题“ How to check if private method is defined in ruby”)。为此,请根据您要检查的内容来使用self.respond_to?(:bar, true)self.class.private_method_defined?(:bar)之类的barModule#private_method_defined?

总结一下,检查称为obj.respond_to?(:bar)()的方法,请

  • defined?(obj.bar)Object#respond_to?)进行鸭类检查
  • obj.class.method_defined?(:bar)(内置defined?
  • obj.methods(true).include?(:bar)Module#method_defined?)用于文字定义检查,不包括细化
  • {{1}}(Object#methods)获取方法列表并进行评估(您可以通过指定false排除包含的模块)