给定一个类,看看实例是否具有方法(Ruby)


227

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

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

即,像

Foo.new.respond_to?(:bar)

但是我觉得有比实例化一个新实例更好的方法。

Answers:


364

我不知道为什么每个人都建议您使用该工具instance_methods以及include?何时进行method_defined?该工作。

class Test
  def hello; end
end

Test.method_defined? :hello #=> true

注意

如果您是从另一种面向对象语言来的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它既是属性又是方法(以及String类型的字符串)。


9
method_defined?是最好的解决方案,因为instance_methods在Ruby 1.8和1.9之间进行了更改。1.8返回一个字符串数组,而1.9返回一个符号数组。method_defined?在1.8和1.9中都使用一个符号。
杰森

2
更不用说:instance_methods会在每次调用时创建一个新数组,而:include?然后将不得不走数组来告诉。相反,:method_defined?将在内部查询方法查找表(C中的一个哈希查找),并在不创建任何新对象的情况下通知您。
Asher

4
像这样使用它至少有一个优点String.instance_methods(false).include? :freeze,但是false参数可以准确说明freeze方法是否在String类中定义。在这种情况下,它将返回false,因为freeze方法是由String从Kernel模块继承的,但String.method_defined? :freeze始终将返回true,这对于实现此目的无济于事。
ugoa

1
@Bane,您将类上的方法与实例上可用的方法混淆了。method_defined?必须在类(例如Array)上使用,但是您尝试在实例(例如[])上使用它
horseyguy 2013年

@banister绝对正确。感谢您的澄清,在阅读时完全有道理。:)我删除了我的评论,以免造成混淆。
班恩

45

您可以使用method_defined?以下方法:

String.method_defined? :upcase # => true

instance_methods.include?其他所有人似乎都建议的要容易,便携和高效。

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


2
请注意,如果您手头有一个实例并且不知道它的类,可以这样做instance.class.method_defined? :upcase
jaydel

25

实际上,这不适用于对象和类。

这样做:

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

3
对于实例,您还可以使用“ instance.class.method_defined?”
Luksurious

这取决于您的红宝石版本。上面的方法适用于所有版本,包括1.8.7。
Alex V

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,那将是添加逻辑以同时搜索字符串和符号的便捷位置。


1
method_defined?更好:)
horseyguy 2010年

@banister-哦,我的,我以某种方式错过了它!xD在@Marc的答案后面加上+1,并添加了一个链接以确保不遗漏:)
Matchu 2010年


3

不知道这是否是最好的方法,但是您始终可以这样做:

Foo.instance_methods.include? 'bar'


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。


2

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


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] 

希望这对您有所帮助!



1

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

在Ruby v2.0 +上,可以使用

Foo.private_instance_methods.include?(:bar) || Foo.instance_methods.include?(:bar)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.