我知道在Ruby中,我可以使用respond_to?来检查对象是否具有特定的方法。
respond_to?
但是,给定类,我如何检查实例是否有特定的方法?
比如,就像
Foo.new.respond_to?(:bar)
但我觉得肯定有比实例化一个新实例更好的方法。
“给定一个类,查看实例是否有方法(Ruby)”的答案更好。显然Ruby内置了这个功能,但我不知何故忽略了它。不管怎样,我的答案留作参考。
Ruby类响应instance_methods和public_instance_methods方法。在Ruby 1.8中,第一个函数以字符串数组的形式列出所有实例方法名,第二个函数将其限制为公共方法。第二个行为是你最可能想要的,因为respond_to?在默认情况下也将自己限制为公共方法。
instance_methods
public_instance_methods
Foo.public_instance_methods.include?('bar')
但是在Ruby 1.9中,这些方法返回符号数组。
Foo.public_instance_methods.include?(:bar)
如果你计划经常这样做,你可能想要扩展Module来包含一个快捷方法。(将它赋值给Module而不是Class似乎很奇怪,但由于instance_methods方法位于那里,所以最好保持该模式一致。)
Module
Class
class Module def instance_respond_to?(method_name) public_instance_methods.include?(method_name) end end
如果您希望同时支持Ruby 1.8和Ruby 1.9,那么在这里添加搜索字符串和符号的逻辑也很方便。
klass.instance_methods.include :method_name或"method_name",我认为这取决于Ruby版本。
klass.instance_methods.include :method_name
"method_name"
不确定这是否是最好的方法,但你总是可以这样做:
Foo.instance_methods.include? 'bar'
尝试Foo.instance_methods.include? :bar
Foo.instance_methods.include? :bar
你可以像下面这样使用method_defined?:
method_defined?
String.method_defined? :upcase # => true
比其他人所建议的instance_methods.include?更容易,可移植和高效。
instance_methods.include?
请记住,你不会知道类是否动态响应了一些method_missing调用,例如通过重新定义respond_to?,或者自Ruby 1.9.2以来通过定义respond_to_missing?。
method_missing
respond_to_missing?
我不知道为什么每个人都建议你应该使用instance_methods和include?,而method_defined?可以工作。
include?
class Test def hello; end end Test.method_defined? :hello #=> true
请注意
如果你是从另一种OO语言学习Ruby的,或者你认为method_defined指的是你显式定义的方法:
method_defined
def my_method end
然后读这个:
在Ruby中,模型上的属性基本上也是一个方法。因此,method_defined?对于属性not just methods. .也将返回true
例如:
给定一个具有String属性first_name的类实例:
first_name
<instance>.first_name.class #=> String <instance>.class.method_defined?(:first_name) #=> true
因为first_name既是一个属性也是一个方法(也是一个string类型的字符串)。
实际上,这并不适用于对象和类。
这样做:
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
如果你正在检查一个对象是否可以响应一系列的方法,你可以这样做:
methods = [:valid?, :chase, :test] def has_methods?(something, methods) methods & something.methods == methods end
methods & something.methods将在它们的公共/匹配元素上连接两个数组。某物Methods包含你要检查的所有方法,它等于Methods。例如:
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。
["my", "methods"]
我认为Rails中的method_defined?有问题。它可能不一致或其他什么,所以如果你使用Rails,最好使用attribute_method?(attribute)中的一些东西。
attribute_method?(attribute)
"测试method_defined?在实例化之前,ActiveRecord类不工作"是关于不一致性的问题。
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]
希望这对你有所帮助!
在我使用ruby 2.5.3的情况下,下面的句子已经完美地工作了:
value = "hello world" value.methods.include? :upcase
它将返回一个布尔值true或false。
虽然respond_to?只会对公共方法返回true,但检查类上的“方法定义”也可能属于私有方法。
在Ruby v2.0+上,可以使用
Foo.private_instance_methods.include?(:bar) || Foo.instance_methods.include?(:bar)