2013-10-18 44 views
2

另一個人asked a similar question但答案並不清楚。代碼合同可以取代參數驗證嗎?

我們的項目中有很多參數驗證代碼。他們大多是零參數檢查公共方法只:

if (param == null) 
{ 
    throw new ArgumentNullException("param"); 
} 

這些檢查不是規範的一部分。它們的主要用途是在編程錯誤的情況下獲得更安全的堆棧轉儲。如果這樣一個簡單的檢查沒有完成,你可以在一個其他圖書館中間的一個隨機點中以NullReferenceException結束十個深度。這使整個回溯到根本原因過程更長。驗證檢查在語義上是清晰的,可以讓您更早地發現問題。

既然我們這麼做了,我們一直在尋找更短的方法來做這樣的檢查。如:

ArgumentHelper.ThrowIfNull(param, "param"); 

或類似的語法。我們希望避免基本上的開銷。 if-throw語法使得代碼不必要的很長。

當我試驗這樣的語法時,我注意到我收斂到類似於斷言和代碼合同的語法。代碼合同非常有吸引力,因爲在開發週期的早期就有可能進行靜態分析和捕獲錯誤。

然而,據我所知代碼合同並沒有設計來補救「輸入驗證」問題或任何類型的驗證問題。 Because the exceptions raised by it are not meant to be caught。他們不是爲了使調試更容易,因爲默認行爲是剝離發佈版本中的這些檢查的代碼。 The rewriter just seems like a hack around it and it's not very performant either

我從所有的理解是「靜態驗證」和「參數驗證」有不同的目標,因此它們不能互換。然而,使用Code.Contracts作爲我的開場白if-then-throw聽起來很自然,我開始不知道自己是否有錯。

我的問題是:使用代碼合同進行參數驗證有效嗎?是否有一個明確的問題,我可以問任何驗證代碼,並提出一個答案,如:「是的,你可以使用代碼合同來檢查這個」和「不,你不能使用代碼合同來檢查」?我如何做出區分?

我想出了「如果我刪除了合同聲明,該功能是否能正常工作?」。這是一個正確的方法嗎?

+0

還檢查了其拋出異常,並提供代碼契約在一個非常乾淨的方式我的擴展方法:http://blog.rsuter.com/elegant-method-parameter-validation- with-code-contracts-support/ –

+0

@RicoSuter您的評論仍需要啓用代碼合同的運行時檢查。看到我的答案爲什麼這是不好的:http://stackoverflow.com/a/25700857/54937 –

+0

不,你有合同和通常的拋出異常,即使運行時驗證被禁用,拋出異常... –

回答

4

嗯。我是那個寫出答案不清楚的人: - /。我會用一種不同的方式來刺激它。

我會說你的方法是正確的,但有一個併發症。從用戶輸入驗證的角度來看這可能會有所幫助。以此代碼:

public void SetUserName(string userInput) 
{ 
    if(String.IsNullOrWhitespace(userInput)) 
     throw new ValidationException("...");  
} 

用戶忘記輸入新用戶名是完全可能的;空字符串是此方法可能接收的預期輸入之一,並且它旨在處理它們。

現在讓我們說,我們添加處理用戶輸入的方法:

public string DoSomethingToUserName(string userInput) 
{ 
    return userInput.ToLower(); 
} 

此方法假定userInput不爲空;正如你所說,如果沒有那麼真實,它將無法工作。如果傳入一個空值,則有人發出編程錯誤。所以,我們可以說,作爲方法的接口的一部分,並得到了靜態驗證,警告我們,如果某人的瘋玩:

public string DoSomethingToUserName(string userInput) 
{ 
    Contract.Requires(userInput != null); 

    return userInput.ToLower(); 
} 

如果這種情況發生在運行時,這不僅意味着沒人會做一個編程錯誤,但是靜態驗證程序無法檢測到問題,或者是由於某些反射使用或類似情況,或者僅僅是因爲其中存在錯誤。因此,至少在公共場合的合同上打開合同重寫總是一個好主意,以防萬一。因此,我們使用契約來檢查參數,只有在沒有它們的情況下該方法不起作用;它們是該方法成功運行的先決條件。如果前提條件失敗了,那麼有人會出錯,並且我們不希望發現異常,因爲它揭示了一個錯誤。

併發症來當DoSomethingToUserName實際上並沒有操縱用戶名本身:

public string DoSomethingToUserName(string userInput) 
{ 
    return DoFirstThingToUserName(userInput); 
} 

private string DoFirstThingToUserName(string userInput) 
{ 
    Contract.Requires(userInput != null); 

    return userInput.ToLower(); 
} 

現在,如果用戶名爲空,DoFirstThingToUserName將失敗; DoSomethingToUserName不在乎它是否是。然而,沒有人能夠通過查看公共接口來判斷userInput必須經過預先驗證。所以我們需要向外界宣佈合同:

public string DoSomethingToUserName(string userInput) 
{ 
    Contract.Requires(userInput != null); 

    return DoFirstThingToUserName(userInput); 
} 

private string DoFirstThingToUserName(string userInput) 
{ 
    Contract.Requires(userInput != null); 

    return userInput.ToLower(); 
} 
1

一個簡單的答案是否定的,你不能用代碼合同替換輸入驗證。主要區別在於輸入驗證不應該對無效數據進入引發異常,因爲它不是特殊情況。另一方面,違反合同總是會在系統中發出錯誤信號,因此應該拋出異常甚至粉碎你的應用(遵循快速失效原則)。

檢查我的博客文章:Code contracts vs input validation

相關問題