2010-08-04 151 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

相關問題