如何判断方法是否在类中定义?

时间:2010-08-04 06:05:42

标签: ruby-on-rails ruby methods defined

class C1
  unless method_defined? :hello  # Certainly, it's not correct. I am asking to find something to do this work.
    def_method(:hello) do
      puts 'Hi Everyone'
    end
  end
end

那么,如何判断方法是否已定义?

3 个答案:

答案 0 :(得分:17)

您发布的代码可以很好地检查方法是否已定义。 Module#method_defined?是正确的选择。 (还有变体Module#public_method_defined?Module#protected_method_defined?Module#private_method_defined?。)问题在于您对def_method的调用,该调用不存在。 (它被称为Module#define_method)。

这就像一个魅力:

class C1      
  define_method(:hello) do
    puts 'Hi Everyone'
  end unless method_defined? :hello
end

但是,由于您事先已知道该名称并且未使用任何闭包,因此无需使用Module#define_method,您只需使用def关键字:

class C1
  def hello
    puts 'Hi Everyone'
  end unless method_defined? :hello
end

或者我误解了你的问题并且你担心遗产?在这种情况下,Module#method_defined?不是正确的选择,因为它遍历整个继承链。在这种情况下,您必须使用Module#instance_methods或其中一个表兄Module#public_instance_methodsModule#protected_instance_methodsModule#private_instance_methods,它们会使用一个可选参数来告诉他们是否包含超类中的方法/ mixins与否。 (请注意,文档是错误的:如果您不传递任何参数,包含所有继承的方法。)

class C1
  unless instance_methods(false).include? :hello
    def hello
      puts 'Hi Everyone'
    end
  end
end

这是一个小测试套件,显示我的建议有效:

require 'test/unit'
class TestDefineMethodConditionally < Test::Unit::TestCase
  def setup
    @c1 = Class.new do
      def self.add_hello(who)
        define_method(:hello) do
          who
        end unless method_defined? :hello
      end
    end

    @o = @c1.new
  end

  def test_that_the_method_doesnt_exist_when_it_hasnt_been_defined_yet
    assert !@c1.method_defined?(:hello)
    assert !@c1.instance_methods.include?(:hello)
    assert !@o.methods.include?(:hello)
    assert !@o.respond_to?(:hello)
    assert_raise(NoMethodError) { @o.hello }
  end

  def test_that_the_method_does_exist_after_it_has_been_defined
    @c1.add_hello 'one'

    assert @c1.method_defined?(:hello)
    assert @c1.instance_methods.include?(:hello)
    assert @o.methods.include?(:hello)
    assert_respond_to @o, :hello
    assert_nothing_raised { @o.hello }
    assert_equal 'one', @o.hello
  end

  def test_that_the_method_cannot_be_redefined
    @c1.add_hello 'one'

    assert @c1.method_defined?(:hello)
    assert @c1.instance_methods.include?(:hello)
    assert @o.methods.include?(:hello)
    assert_respond_to @o, :hello
    assert_nothing_raised { @o.hello }
    assert_equal 'one', @o.hello

    @c1.add_hello 'two'

    assert @c1.method_defined?(:hello)
    assert @c1.instance_methods.include?(:hello)
    assert @o.methods.include?(:hello)
    assert_respond_to @o, :hello
    assert_nothing_raised { @o.hello }
    assert_equal 'one', @o.hello, 'it should *still* respond with "one"!'
  end
end

答案 1 :(得分:2)

查看Ruby Object class。它有methods函数来获取方法列表,respond_to?来检查特定方法。所以你想要这样的代码:

class C1
  def add_hello
    unless self.respond_to? "hello"
      def hello
        puts 'Hi Everyone'
      end
    end  
  end
end

cone.hello      #This would fail
cone.add_hello  
cone.hello      #This would work

答案 2 :(得分:1)

Object类具有方法“methods”:docs

 class Klass
   def kMethod()
   end
 end
 k = Klass.new
 k.methods[0..9]    #=> ["kMethod", "freeze", "nil?", "is_a?",
                    #    "class", "instance_variable_set",
                    #    "methods", "extend", "__send__", "instance_eval"]
 k.methods.length   #=> 42