クラスを指定して、インスタンスにメソッドがあるかどうかを確認します(Ruby)


227

Rubyでrespond_to?、オブジェクトに特定のメソッドがあるかどうかを確認するために使用できることを知っています。

しかし、クラスが与えられた場合、インスタンスに特定のメソッドがあるかどうかをどのように確認できますか?

つまり、次のようなもの

Foo.new.respond_to?(:bar)

しかし、私は新しいインスタンスをインスタンス化するよりも良い方法があるはずだと感じています。

回答:


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の両方でシンボルを取ります。
Jason

2
言うまでもなく、:instance_methodsは呼び出しごとに新しい配列を作成し、その:includeですか?次に、配列を調べて指示する必要があります。対照的に、:method_defined?メソッドルックアップテーブル(Cのハッシュルックアップの1つ)を内部的に照会し、新しいオブジェクトを作成せずに通知します。
アシャー2013

4
このように使用すると、少なくとも1つの利点がありますがString.instance_methods(false).include? :freezefalse引数は、freezeメソッドがStringクラスで定義されているかどうかを正確に示します。この場合、freezeメソッドはカーネルモジュールから文字列によって継承されるため、falseをString.method_defined? :freeze返しますが、常にtrueを返します。これは、このような目的にはあまり役立ちません。
ugoa 2013

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?」を使用することもできます
魅力的な

Rubyのバージョンによって異なります。上記のこの方法は、1.8.7を含むすべてのバージョンで機能します。
Alex V

10

クラスが与えられたら、インスタンスにメソッド(Ruby)があるかどうかを確認する」への回答の方が優れています。どうやらRubyにはこの機能が組み込まれており、どういうわけかそれを逃しました。いずれにせよ、私の答えは参考にさせていただきます。

Rubyクラスはメソッドinstance_methodsとに応答しますpublic_instance_methods。Ruby 1.8では、1つ目はすべてのインスタンスメソッド名を文字列の配列でリストし、2つ目はパブリックメソッドに制限します。2番目の動作も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


3

これが最善の方法かどうかはわかりませんが、いつでもこれを行うことができます:

Foo.instance_methods.include? 'bar'


3

オブジェクトが一連のメソッドに応答できるかどうかを確認する場合は、次のようにすることができます。

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

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

これによりmethods & something.methods、2つの配列が共通/一致する要素で結合されます。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

私の場合、ruby 2.5.3を使用すると、次の文が完全に機能します。

value = "hello world"

value.methods.include? :upcase

ブール値trueまたはfalseを返します。


1

一方ではrespond_to?、プライベートメソッドに関連するクラスの「メソッドの定義」をチェックし、パブリックメソッドのみのためにtrueを返します。

Ruby v2.0 +では、パブリックセットとプライベートセットの両方のチェックは、

Foo.private_instance_methods.include?(:bar) || Foo.instance_methods.include?(:bar)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.