共用方式為


本文章是由機器翻譯。

切邊

為您的類別提供軟體合約

Dino Esposito

軟體開發的舊但很好的作法建議您將最前面的每一種方法 — 任何重大問題發生之前,條件式陳述式的障礙。每個條件式陳述式會檢查輸入的值必須驗證的其他條件。如果不驗證條件,程式碼會擲回例外狀況。這個模式通常被指如果接著擲回。

不過如果-然後-Throw 那麼我們要撰寫有效率且正確的程式碼呢?它是不夠應付所有狀況嗎?

它可能會很耗時 是不夠應付所有狀況不是一個新。透過契約 (雙位元組字元) 的設計是一種方法介紹幾個年前由小姐慧君每套軟體有合約概念為基礎的它正式說明了它所預期與它所提供。如果接著擲回模式幾乎涵蓋第一個部分的合約。 它完全缺少第二個部分。雙位元組字元可採用原始格式不支援任何主流的程式語言。不過,架構會有可以讓您 taste 的雙位元組字元在常用的語言,例如 Java]、 [Perl]、 [並列文字]、 [JavaScript,並課程,Microsoft 的類別。NET Framework 語言。在中。NET 中,您執行雙位元組字元透過加入程式碼合約程式庫。NET 架構 4,位於 mscorlib 組件中。請注意文件庫使用 Silverlight 4 應用程式而非 Windows 電話應用程式。

我相信幾乎每位開發人員會同意開發合約第一個方法是,這些十分重要的原則。但是我不想讓許多正在使用中的程式碼合約。現在,Microsoft 已盡軟體合約,可以使用和在 Visual Studio 整合式 NET 4 應用程式。這篇文章著重於程式碼可維護性合約第一個方法的優點和易用性開發。您希望可以使用此文件中的引數,將程式碼合約賣給您的老闆下, 一個專案。在未來本篇文章中期中,我將深入層面,例如組態、 執行階段工具和程式設計功能,例如繼承。

推理關於簡單計算機類別

程式碼合約是注意的一種狀態。 您不應該將它們放保留直到之所以稱為來設計需要進階的架構和許多尖端技術的大型應用程式。請注意,— 時卻管理不當,即使看似最強大的技術可能會造成問題。程式碼合約適用於幾乎任何類型的應用程式,只要您已經學會它們很好。所以舉一個簡單的類別 — 傳統的 [小算盤] 類別,如下:

public class Calculator
{
    public Int32 Sum(Int32 x, Int32 y)
    {
        return x + y;
    }

    public Int32 Divide(Int32 x, Int32 y)
    {
        return x / y;
    }
}

您可能會同意的程式碼不實際可行,因為它缺少至少一個重要的項目:若要查看是否您嘗試以零為除數核取。 當我們撰寫更好它的版本時,我們也假設我們需要處理的其他問題:[小算盤] 不支援負數值。 圖 1 有更新的版本的程式碼,加入幾個 If 然後 Throw 陳述式。

圖 1實作 If 然後 Throw 模式的 [小算盤] 類別

public class Calculator
{
    public Int32 Sum(Int32 x, Int32 y)
    {
        // Check input values
        if (x <0 || y <0)
            throw new ArgumentException();


        // Perform the operation
        return x + y;
    }

    public Int32 Divide(Int32 x, Int32 y)
    {
        // Check input values
        if (x < 0 || y < 0)
            throw new ArgumentException();
        if (y == 0)
            throw new ArgumentException();

        // Perform the operation  
        return x / y;
    }
}

截至目前為止,我們可以指明類別可能會開始處理輸入的資料或在輸入無效的情況下就會擲只回之前的任何項目。 由類別產生的結果呢? 我們怎麼知道仲呢? 查看規格,我們應該可以預期這兩種方法傳回值不小於零。 我們如何可以強制執行,以及如果它並不會發生失敗? 我們需要第三個版本的程式碼,如所示 圖 2

圖 2檢查先決條件和達成條件來形容 [小算盤] 類別

public class Calculator
{
    public Int32 Sum(Int32 x, Int32 y)
    {
        // Check input values
        if (x <0 || y <0)
            throw new ArgumentException();

        // Perform the operation
        Int32 result = x + y;

        // Check output
        if (result <0)
            throw new ArgumentException();

        return result;
    }

    public Int32 Divide(Int32 x, Int32 y)
    {
        // Check input values
        if (x < 0 || y < 0)
            throw new ArgumentException();
        if (y == 0)
            throw new ArgumentException();

        // Perform the operation
        Int32 result = x / y;

        // Check output
        if (result < 0)
            throw new ArgumentException();

        return result;
    }
}

這兩個方法現在會呈現出來三個主要階段:請檢查輸入的值、 作業的效能以及核取的輸出。 檢查輸入和輸出有兩個不同的用途。 輸入會檢查您的呼叫端程式碼中的旗標錯誤。 輸出檢查尋找自己的程式碼中的錯誤。 您真的需要檢查輸出嗎? 我承認可驗證檢查條件,透過一些單元測試中的判斷提示。 在這種情況下,您完全不需要埋在執行階段程式碼中這種檢查。 不過,在程式碼中有檢查讓類別自我描述,可以很清楚可以和無法做 — 就像是簽約服務中的條款。

如果您比較的原始程式碼 圖 2 與我們開始的簡單類別,您會看到來源成長的很多行 — 而且這是具有幾個要求,以滿足的簡單類別。 讓我們來看它一個步驟進一步。

圖 2,我們識別出 (輸入的核取、 作業和核取輸出) 的三個步驟依序執行。 如果作業的效能是不夠複雜,無法容納額外的結束點吗? 如果其中幾個結束點參考其他結果需要的地方的錯誤情況? 變真的得很複雜。 若要可說明此點,不過,它滿足我們將快顯結束新增到其中一個方法,如所示 圖 3

圖 3 快顯結束複製達成條件來形容的程式碼

public Int32 Sum(Int32 x, Int32 y)
{
    // Check input values
    if (x <0 || y <0)
        throw new ArgumentException();
            
    // Shortcut exit
    if (x == y)
    {
        // Perform the operation
        var temp = x <<1; // Optimization for 2*x

        // Check output
        if (temp <0)
            throw new ArgumentException();

        return temp;
    }

    // Perform the operation
    var result = x + y;

    // Check output
    if (result <0)
        throw new ArgumentException();

    return result;
}

範例程式碼 (和它 只是範例),方法加總嘗試捷徑,如果兩個值相等 — 而不是將之加相乘。 不過,用來檢查輸出值的程式碼必須複寫的程式碼中每個早期結束路徑。

底線代表沒有人可以合理地將合約第一種方法不需要一些嚴重的工具或至少一個特定的協助程式架構的軟體開發。 檢查初步條件相當容易,成本也執行; 以手動方式處理後置執行條件將整個程式碼基底不易處理及錯誤很容易出錯。 奇思會使類別的原始程式碼的合約的幾個其他輔助方面對於開發人員,例如檢查條件,當輸入的參數都是集合,並確保類別永遠處於已知的有效狀態,只要方法或屬性稱為淌真實。

輸入程式碼合約

在中。NET 架構 4、 程式碼合約是一種架構,提供更方便的語法來表示類別協定。 特別是,程式碼合約支援三種類型的合約:先決條件、 達成條件來形容和不變項目的。 先決條件指出應該驗證的方法來安全地執行初步條件。 達成條件來形容表示一旦正確或因為擲回的例外狀況的執行方法應該要驗證的條件。 最後,不變量,將告訴您,在任何類別執行個體的存留期內恆為 true 的條件。 更準確的說,不變量,指出要守在每個類別和用戶端之間的可能互動後的條件 — 也就在執行後的公用成員,包括建構函式。 表示為不變項目的條件不檢查與接下來可能會暫時違反私用成員引動過程之後。

程式碼合約 API 包含一份合約類別上定義的靜態方法。 您使用需要方法,以表示先決條件和表達達成條件來形容確認。 圖 4 示範如何重寫使用的程式碼合約的 [小算盤] 類別。

圖 4 撰寫使用程式碼合約 [小算盤] 類別

using System.Diagnostics.Contracts;
public class Calculator
{
    public Int32 Sum(Int32 x, Int32 y)
    {
        Contract.Requires<ArgumentOutOfRangeException>(x >= 0 && y >= 0);
        Contract.Ensures(Contract.Result<Int32>() >= 0);

        if (x == y)
            return 2 * x;

        return x + y;
    }

    public Int32 Divide(Int32 x, Int32 y)
    {
        Contract.Requires<ArgumentOutOfRangeException>(x >= 0 && y >= 0);
        Contract.Requires<ArgumentOutOfRangeException>(y > 0);

        Contract.Ensures(Contract.Result<Int32>() >= 0);

        return x / y;
    }
}

快速比較 圖 3圖 4 實作雙位元組字元會顯示有效的 API 的威力。 方法程式碼上一步是在其中您分辨只有兩個層級的高度可讀格式:合約資訊包括先決條件和達成條件來形容和實際的行為。 您不必混合條件和行為,如 圖 3。 如此一來,大幅改善可讀性,並維護這個程式碼取得小組更容易。 比方說,您可以在這裡可以迅速且安全地加入新的指定條件或編輯達成條件來形容隨心所欲 — 您介入在一個位置,並可以清楚地追蹤您的變更。

合約資訊是透過一般的 C# 或 Visual Basic 程式碼來表示。 合約指示不像傳統的宣告式屬性,但是它們仍可維持強式的宣告式類別。 使用一般的程式碼,而不屬性提高開發人員,程式設計能力,因為它可更為自然來表示您必須記住的條件。 在此同時,使用程式碼合約提供更多指引重整程式碼。 程式碼合約,事實上,表示您應該會從方法的行為。 有助於維護程式碼撰寫的法則,當您撰寫方法,協助保護您的程式碼可讀取,即使先決條件和達成條件來形容中取得許多。 雖然您可以用合約使用如中的高階語法來表示 圖 4,當實際取得編譯程式碼,產生的流量不能與所述的程式碼 圖 3。 然後所在這一輪?

其他工具整合在 Visual Studio 建置程序中,程式碼合約已經 — 不會重整程式碼,了解明示的先決條件和達成條件來形容的預定的用途的技巧並延伸到適當的程式碼區塊放在所邏輯上隸屬。 身為開發人員,您就不必擔心放置 postcondition 的位置,以及複製它,如果在某個時刻,編輯程式碼,以新增另一個結束點的位置。

表示條件

您可以找出正確的語法先決條件和達成條件來形容從程式碼合約文件; 您可以從在 DevLabs 網站取得最新的 PDF bit.ly/f4LxHi. 我會簡短地摘要說明它。 您可以使用下列方法來表示必要的狀況,否則則擲回指定的例外狀況:

Contract.Requires<TException> (Boolean condition)

方法具有您可能要考慮的幾個多載。 一 postcondition 方法確認 expresses:

Contract.Ensures(Boolean condition)

進行撰寫先前條件時,將通常會包含運算式,只輸入的參數,可能是某些其他方法或屬性相同類別中。 若發生這種情況,您被要求裝飾這個方法時要注意執行方法並不會修改物件的狀態的純虛擬屬性。 請注意程式碼合約工具假設屬性 getter 純。

當您撰寫 postcondition 時,您可能需要存取其他資訊,例如所傳回的值或區域變數的起始值。 只要透過臨機操作的方法,例如 Contract.Result <T> 若要取得型別 T) 的值所傳回的方法和 Contract.OldValue <T> 若要取得儲存在指定的本機變數的執行方法開頭的值。 最後,您也有機會確認條件的方法執行期間擲回例外狀況時。 在這種情況下,您使用的方法 Contract.EnsuresOnThrow <TException>。

Abbreviators

合約語法當然更加精簡比使用一般的程式碼,不過是可以增加大型也。 在這種情況下,可讀性都會有危險一次。 自然的補救辦法群組在副程式中的數個合約指示,如所示 圖 5

圖 5使用 ContractAbbreviators

public class Calculator
{
    public Int32 Sum(Int32 x, Int32 y)
    {
        // Check input values
        ValidateOperands(x, y);
        ValidateResult();

        // Perform the operation
        if (x == y)
            return x<<1; 
        return x + y;
    }

    public Int32 Divide(Int32 x, Int32 y)
    {
        // Check input values   
        ValidateOperandsForDivision(x, y);
        ValidateResult();

        // Perform the operation
        return x / y;
    }

    [ContractAbbreviator]

    private void ValidateOperands(Int32 x, Int32 y)
    {
        Contract.Requires<ArgumentOutOfRangeException>(x >= 0 && y >= 0);
    }

    [ContractAbbreviator]
    private void ValidateOperandsForDivision(Int32 x, Int32 y)
    {
        Contract.Requires<ArgumentOutOfRangeException>(x >= 0 && y >= 0);
        Contract.Requires<ArgumentOutOfRangeException>(y > 0);
    }

    [ContractAbbreviator]

    private void ValidateResult()
    {
        Contract.Ensures(Contract.Result<Int32>() >= 0);
    }
}

ContractAbbreviator 屬性指示產品有關如何正確地解譯裝飾的方法。 不具有來限定它視為一種巨集來展開,事實上,ValidateResult 方法的屬性 (和其他 ValidateXxx 方法,在 圖 5) 包含而是 inextricable 的程式碼。 什麼會,比方說,Contract.Result <T> 參照,因為它已用於 void 方法吗? 目前,ContractAbbreviator 屬性必須是明確定義的開發人員在專案中,因為它不包含在 mscorlib 組件。 類別是相當簡單:

namespace System.Diagnostics.Contracts
{
    [AttributeUsage(AttributeTargets.Method, 
      AllowMultiple = false)]
    [Conditional("CONTRACTS_FULL")]
    internal sealed class 
      ContractAbbreviatorAttribute : 
      System.Attribute
    {
    }
}

初始狀態、 改良的程式碼

加總,程式碼合約 API — 基本上合約類別 — 也就是原生的。NET 架構 4,因為它屬於 mscorlib 組件。 Visual Studio 2010 隨附的程式碼合約組態中特定的專案屬性的設定] 頁面。 針對每個專案,您必須到此檢視,並明確地啟用執行階段檢查的合約。 您也需要從 DevLabs 網站下載執行階段工具。 一次在這個網站,您挑選您有的 Visual Studio 版本的適當安裝程式。 執行階段工具包括程式碼合約產品和介面產生器,再加上靜態檢查程式。

程式碼合約幫助您撰寫清除程式碼強制執行您指出預期的行為,且每個方法的結果。 在非常小的這會提供指引,當您移至重構並改善您的程式碼。 還有一大堆多程式碼合約的相關討論。 特別是,在本文中,我很快提到恆定性,根本沒有提到強大的功能合約繼承。 在未來我打算涵蓋所有這及其他文件。 下次見 !

Dino Esposito 是作者的 < 程式設計 Microsoft ASP。NET MVC"(在 [微軟出版品,2010年) 和 coauthor 的"Microsoft。NET 中: 架構應用程式企業的"(在 [微軟出版品,2008年)。居住在義大利,Esposito 是在世界各地的產業活動演說。 在 Twitter 上都追隨他 twitter.com/despos.

感謝至下列技術專家檢閱這份文件: Brian Grunkemeyer