Для данного класса посмотрите, есть ли у экземпляра метод (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 с другого языка OO или вы думаете, что это 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) и сообщит вам, не создавая никаких новых объектов.
Ашер

4
Хотя при его использовании, по крайней мере, есть одно преимущество String.instance_methods(false).include? :freeze, ложный аргумент может точно сказать, определен ли freezeметод в классе String или нет. В этом случае он вернет false, потому что freezeметод унаследован от модуля Kernel от String, но String.method_defined? :freezeвсегда вернет true, что не может сильно помочь с такой целью.
Угоа

1
@Bane, вы путаете методы класса с методами, доступными в экземпляре. method_defined?должен быть использован в классе (например Array), но вы пытаетесь использовать его в экземплярах (например [])
horseyguy

@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

Это зависит от вашей версии ruby. Этот метод выше работает для всех версий, включая 1.8.7.
Алекс V

10

Ответ на « Данный класс, посмотреть, если экземпляр имеет метод (Ruby) » лучше. Очевидно, в Ruby есть эта встроенная система, и я почему-то пропустил ее. Мой ответ оставлен для справки, вне зависимости.

Классы Ruby отвечают на методы instance_methodsи public_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?лучше :)
скакун

@banister - о, мой, и я как-то пропустил это! xD бросил +1 за ответ @ Marc и добавил ссылку, чтобы убедиться, что он не пропущен :)
Matchu


3

Не уверен, что это лучший способ, но вы всегда можете сделать это:

Foo.instance_methods.include? 'bar'

3

Я думаю, что с method_defined?Rails что-то не так . Это может быть непоследовательным или что-то, поэтому, если вы используете Rails, лучше использовать что-то изattribute_method?(attribute) .

« Проверка для method_defined?» в классах ActiveRecord не работает до момента создания экземпляра »- вопрос о несоответствии.


3

Если вы проверяете, может ли объект отвечать на ряд методов, вы можете сделать что-то вроде:

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

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

the 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

В моем случае, работающем с 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)
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.