'回应?' 与'定义?'

saw*_*awa 3 ruby methods metaprogramming

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

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

哪个更好?除了效率之外还有其他问题需要考虑吗?

Jör*_*tag 9

如果我想检查是否定义了具有给定名称的方法,哪个更好用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
Run Code Online (Sandbox Code Playgroud)


Mas*_*ano 5

我的简短回答是我会说。然而,Module#method_defined?也许在大多数情况下并不适合。\n这取决于您到底想知道什么。这是一个很长的答案,解释了目的。

\n\n

我想最流行的目的是Duck-typing,\n即检查一个对象是否响应具有特定名称的方法,如果是,那么你不关心它实际上是什么类的对象或在哪里/该方法如何在内部实现。在这种情况下,Object#respond_to? \nis(我认为)是为其设计的确切方法(正如方法名称所暗示的那样)。确实(正如 @J\xc3\xb6rg W Mittag 指出的),如果Object#respond_to_missing?结果可能会改变。在其类中重新定义。然而,这就是 \xe2\x80\x94 将方法注册为有效 \xe2\x80\x94 的全部目的respond_to_missing?,例如,参见Ruby 核心开发人员 Marc-Andr 的(旧)博客文章“ Method_missing,礼貌地” xc3\xa9 进行澄清。

\n\n

另一种传统方法(自 Ruby 1 起)是使用 Ruby 内置defined?. 过去对“在 Ruby 中,如何检查方法 \xe2\x80\x9cfoo=()\xe2\x80\x9d 是否已定义?”的回答 解释了特定于 的一个潜在有用的情况defined?,即判断它是否是赋值。为了检查方法是否存在,其工作方式与大多数(或所有?)情况defined?类似,只是它返回 String 对象或nil而不是 Boolean。我想说这取决于个人喜好;然而,我总体上赞成,因为它显然更具体,因此更具可读性,并且实际打字时出现潜在问题(例如忘记 的一对括号)的可能性更小。Object#respond_to?Object#respond_to?defined?

\n\n

或者,要检查对象的类是否在其自身、其超类和包含的模块之一中显式定义了方法及其名称,请使用Module#method_define? 。具体来说,此方法忽略respond_to_missing?,这意味着在实践中任何使用BasicObject#method_missing 的元编程(例如,Rails 的 ActiveRecord 广泛使用)都会被忽略。

\n\n

另外,从 Ruby 2.6.5 开始,这Module#method_defined?会忽略细化,即 引入的模块方法using被视为未定义,与Object#respond_to?和 相比,内置defined?,两者都将这些细化-d方法视为已定义。

\n\n

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

\n\n

关于私有方法的另一条评论,其中包括内置函数(也称为内核方法)。\n Object#respond_to? 默认情况下不响应私有方法,除非第二个参数指定为 true(请参阅过去的问题“如何检查 ruby​​ 中是否定义了私有方法”)。为此,请使用self.respond_to?(:bar, true)Module#private_method_define? 就像self.class.private_method_defined?(:bar),具体取决于您要检查的内容。

\n\n

总而言之,要检查调用bar() 的方法,请执行以下操作

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