2010-08-04 80 views

回答

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_methods,Module#protected_instance_methodsModule#private_instance_methods,它们采用可选参数告诉他们是否包含来自超类/ mixin的方法。 (请注意,文件是错误的:如果你没有传递参数,它包括所有继承的方法。)

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 [email protected]_defined?(:hello) 
    assert [email protected]_methods.include?(:hello) 
    assert [email protected]?(:hello) 
    assert [email protected]_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 
+0

测试通过1.9.2,但'test_that_the_method_cannot_be_redefined'和'test_that_the_method_does_exist_after_it_has_been_defined'在红宝石1.8.7下失败。 – mrm 2011-08-22 18:27:40

1

Object类有方法 “方法”: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 
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 
+0

-1,原因有四:1)没有解决OP问题。使用'method_defined?'就好,问题在于他拼写了'define_method'。 2)'respond_to?'不检查特定的方法,它检查对象是否响应特定的消息。 (提示:名称sorta会将它给出,你不觉得吗?)理解方法和消息之间的区别对于理解Ruby甚至是OO是基本的。 3)在你的代码中,你检查类对象'C1'是否响应':hello',并基于你定义'''*'的*实例*的'hello'方法。 ... – 2010-08-04 08:21:12

+0

...再一次:理解*实例*和*类之间的区别*是理解Ruby和基于类的OO的基础。 4)您的测试套件实际上不会测试OP关心的事情,即您无法两次定义该方法。你只测试你可以定义一次方法,但这不是问题。 – 2010-08-04 08:23:32

+0

@jorg,他把'respond_to?'放在一个实例方法('add_hello')中,所以它检查实例(而不是类)。另外,出于好奇,在Ruby中发送消息和调用方法有什么区别? :) – horseyguy 2010-08-04 12:41:05

相关问题