2012-03-22 20 views
21

由於Ruby中沒有類型,Ruby程序員如何確保函數接收正確的參數?現在,我正在重複執行if object.kind_of/instance_of語句來檢查並隨處增加運行時錯誤,這很醜陋。必須有更好的方式來做到這一點。Ruby程序員如何進行類型檢查?

+0

爲什麼你需要確保輸入是特定類型的?函數做他們被告知的事情,不應該被迫輸入檢查。 – Blender 2012-03-22 05:23:50

+3

查閱「鴨子打字」 – 2012-03-22 05:24:30

+0

@Blender例如。 setNextNode(link)。在這個函數中,如果我沒有類型檢查任何東西都可以傳入......我現在正在做一個任務,並且我被告知要進行類型檢查並確保運行時安全。 – kun 2012-03-22 05:35:31

回答

18

Ruby當然是動態類型的。

因此,方法文件確定類型合同;類型信息從正式類型系統移到[方法文檔中的非正式類型說明]。我將「行爲像一個數組」這樣的概念和諸如「是一個字符串」等細節混爲一談。來電者只能期望與聲明的類型一起工作。

如果主叫方違反此合同,則可能發生任何事情。該方法不用擔心:它被錯誤地使用了。

在上述的光,我避免檢查用於特定類型和避免試圖創建與這種行爲重載。

單元測試可以幫助確保合同適用於預期數據。

+7

@cjk雖然這是一個很好的範例,但它仍然沒有回答這個問題。我建議你接受薩瓦的回答。 – anthropomorphic 2013-06-24 06:43:40

20

我個人的方式,我不確定它是否是一種推薦的方式,一般是在發生錯誤時進行類型檢查並執行其他驗證。我把類型檢查例程放在一個救援塊中。這樣,當給出正確的參數時,我可以避免性能損失,但在發生錯誤時仍然會返回正確的錯誤消息。

def foo arg1, arg2, arg3 
    ... 
    main_routine 
    ... 
rescue 
    ## check for type and other validations 
    raise "Expecting an array: #{arg1.inspect}" unless arg1.kind_of?(Array) 
    raise "The first argument must be of length 2: #{arg1.inspect}" unless arg1.length == 2 
    raise "Expecting a string: #{arg2.inspect}" unless arg2.kind_of?(String) 
    raise "The second argument must not be empty" if arg2.empty? 
    ... 
    raise "This is `foo''s bug. Something unexpected happened: #{$!.message}" 
end 

假設在main_routine,可以使用該方法eacharg1假設arg1是一個數組。如果事實證明這是其他問題,那麼each未定義,那麼裸露的錯誤消息將類似method each not defined on ...,從方法foo的用戶角度來看,這可能沒有幫助。在這種情況下,原始錯誤消息將被消息Expecting an array: ...替代,這更有幫助。

+4

但是,這個問題是由於其他問題被拾取而導致*真實*錯誤可能被掩蓋。所以,雖然它也可能成爲一個問題,但它可能與實際提出的內容無關。如果要完成明確的類型檢查,首先*執行,而不是恢復。 – 2012-03-22 06:39:21

+2

爲什麼使用'#{arg1}'而不是'#{arg1.inspect}'?或者更具體地說,''期待一個字符串:#{arg2}「'而不是'」期待一個字符串:#{arg2.inspect}「'? – 2012-03-22 22:48:07

+1

@pst當我編輯時,可以通過在末尾加上另一個「raise」來避免。如果在'foo'內部引發錯誤,但所有驗證通過,則驗證檢查不夠或者'foo'內部存在錯誤。 – sawa 2013-03-09 13:03:54

8

如果一個方法有一個存在的理由,它將被調用。

如果編寫了合理的測試,一切都會被調用。

如果每個方法都被調用,那麼每個方法都將進行類型檢查。

不要浪費時間放入可能不必要地限制呼叫者的類型檢查,而只是重複運行時檢查。花時間寫作測試。

1

我建議在方法的使用開始加註到手動添加類型檢查,簡單而有效:

def foo(bar) 
    raise TypeError, "You called foo without the bar:String needed" unless bar.is_a? String 
    bar.upcase 
end 

最好的方法,當你沒有太多的參數,也是一個建議是使用關鍵字如果你有多個參數,並且注意其當前/將來的實現細節,那麼它們可以在ruby 2+上得到,他們正在改善這種情況,給程序員一種方法來看看值是否爲零。

加:您可以使用自定義異常

class NotStringError < TypeError 
    def message 
    "be creative, use metaprogramming ;)" 
#... 
raise NotStringError