242

我知道在 Ruby 中我可以respond_to?用来检查一个对象是否有某种方法。

但是,给定类,我如何检查实例是否具有某种方法?

即,类似

Foo.new.respond_to?(:bar)

但我觉得肯定有比实例化新实例更好的方法。

4

12 回答 12

379

我不知道为什么每个人都建议您应该使用instance_methods以及include?何时method_defined?完成这项工作。

class Test
  def hello; end
end

Test.method_defined? :hello #=> true

笔记

如果您从另一种 OO 语言开始使用 Ruby,或者您认为这method_defined仅意味着您明确定义的方法:

def my_method
end

然后阅读:

在 Ruby 中,模型上的属性(属性)基本上也是一种方法。所以method_defined?也会为属性返回 true,而不仅仅是方法。

例如:

给定一个具有 String 属性的类的实例first_name

<instance>.first_name.class #=> String

<instance>.class.method_defined?(:first_name) #=> true

因为first_name既是属性又是方法(以及字符串类型的字符串)。

于 2010-07-21T23:28:35.717 回答
45

您可以method_defined?按如下方式使用:

String.method_defined? :upcase # => true

instance_methods.include?其他人似乎暗示的要容易、便携和高效。

请记住,您不会知道一个类是否动态响应某些调用method_missing,例如通过重新定义respond_to?,或者从 Ruby 1.9.2 开始通过定义respond_to_missing?.

于 2010-07-21T23:27:22.153 回答
26

实际上,这对对象和类都不起作用。

这样做:

class TestClass
  def methodName
  end
end

因此,对于给定的答案,这有效:

TestClass.method_defined? :methodName # => TRUE

但这不起作用:

t = TestClass.new
t.method_defined? : methodName  # => ERROR!

所以我将它用于类和对象:

课程:

TestClass.methods.include? 'methodName'  # => TRUE

对象:

t = TestClass.new
t.methods.include? 'methodName'  # => TRUE
于 2013-10-10T20:34:38.183 回答
10

“给定一个类,看看实例是否有方法(Ruby) ”的答案更好。显然 Ruby 有这个内置的,我不知何故错过了它。无论如何,我的答案仅供参考。

Ruby 类响应方法instance_methodspublic_instance_methods. 在 Ruby 1.8 中,第一个在字符串数组中列出所有实例方法名称,第二个将其限制为公共方法。第二种行为是您最可能想要的,因为respond_to?默认情况下也将自身限制为公共方法。

Foo.public_instance_methods.include?('bar')

但是,在 Ruby 1.9 中,这些方法返回符号数组。

Foo.public_instance_methods.include?(:bar)

如果您打算经常这样做,您可能需要扩展Module以包含快捷方式。Module(将其分配给而不是似乎很奇怪Class,但由于这是instance_methods方法所在的位置,因此最好与该模式保持一致。)

class Module
  def instance_respond_to?(method_name)
    public_instance_methods.include?(method_name)
  end
end

如果您想同时支持 Ruby 1.8 和 Ruby 1.9,那么这将是一个方便的地方,可以添加搜索字符串和符号的逻辑。

于 2010-07-21T20:09:32.193 回答
5

尝试Foo.instance_methods.include? :bar

于 2010-07-21T20:11:38.557 回答
3

不确定这是否是最好的方法,但你总是可以这样做:

Foo.instance_methods.include? 'bar'
于 2010-07-21T20:10:23.393 回答
3

如果您正在检查一个对象是否可以响应一系列方法,您可以执行以下操作:

methods = [:valid?, :chase, :test]

def has_methods?(something, methods)
  methods & something.methods == methods
end

methods & something.methods在它们的共同/匹配元素上加入两个数组。something.methods 包括您正在检查的所有方法,它将等于方法。例如:

[1,2] & [1,2,3,4,5]
==> [1,2]

所以

[1,2] & [1,2,3,4,5] == [1,2]
==> true

在这种情况下,你会想要使用符号,因为当你调用 .methods 时,它会返回一个符号数组,如果你使用["my", "methods"]了,它会返回 false。

于 2014-01-27T11:29:04.977 回答
3

我认为method_defined?Rails 有问题。它可能不一致或什么,所以如果你使用 Rails,最好使用来自attribute_method?(attribute).

在实例化之前在 ActiveRecord 类上测试 method_defined? ”是关于不一致的问题。

于 2014-06-05T10:50:49.897 回答
2

klass.instance_methods.include :method_name或者"method_name",取决于我认为的 Ruby 版本。

于 2010-07-21T20:09:40.353 回答
2
class Foo
 def self.fclass_method
 end
 def finstance_method
 end
end

foo_obj = Foo.new
foo_obj.class.methods(false)
=> [:fclass_method] 

foo_obj.class.instance_methods(false)
=> [:fclass_method] 

希望这对你有帮助!

于 2014-06-05T12:06:44.727 回答
2

虽然respond_to?只会对公共方法返回 true,但检查类上的“方法定义”也可能与私有方法有关。

在 Ruby v2.0+ 上,可以通过以下方式检查公共集和私有集

Foo.private_instance_methods.include?(:bar) || Foo.instance_methods.include?(:bar)
于 2020-02-21T11:22:24.600 回答
1

在我使用 ruby​​ 2.5.3 的情况下,以下句子效果很好:

value = "hello world"

value.methods.include? :upcase

它将返回一个布尔值 true 或 false。

于 2019-01-21T02:30:50.103 回答