2012-07-25 59 views
1

我已經在MSDN上的CodeContracts forum上發佈了這個消息,但是顯然沒有人知道或者不願意研究這個問題。CodeContracts:假設/斷言的重用?

我試圖減少重複斷言並使其更加可重用,但不幸的是這不起作用,你能解釋爲什麼嗎?

[ContractVerification(false)] 
public static class Assert 
{ 
    [Conditional("DEBUG")] 
    public static void GreaterThan<T>(T value, T lowerBound) where T : IComparable<T> 
    { 
     Contract.Ensures(value.CompareTo(lowerBound) > 0); 
    } 

    [Conditional("DEBUG")] 
    public static void GreaterThanOrEqual<T>(T value, T lowerBound) where T : IComparable<T> 
    { 
     Contract.Ensures(value.CompareTo(lowerBound) >= 0); 
    } 

    [Conditional("DEBUG")] 
    public static void LessThan<T>(T value, T upperBound) where T : IComparable<T> 
    { 
     Contract.Ensures(value.CompareTo(upperBound) < 0); 
    } 

    [Conditional("DEBUG")] 
    public static void LessThanOrEqual<T>(T value, T upperBound) where T : IComparable<T> 
    { 
     Contract.Ensures(value.CompareTo(upperBound) <= 0); 
    } 

    [Conditional("DEBUG")] 
    public static void NotNull(object value) 
    { 
     Contract.Ensures(value != null); 
    } 

    [Conditional("DEBUG")] 
    public static void NotNullOrEmpty(string value) 
    { 
     Contract.Ensures(!string.IsNullOrEmpty(value)); 
    } 

    [Conditional("DEBUG")] 
    public static void True(bool value) 
    { 
     Contract.Ensures(value); 
    } 

    [Conditional("DEBUG")] 
    public static void False(bool value) 
    { 
     Contract.Ensures(!value); 
    } 

    [Conditional("DEBUG")] 
    public static void InRange<T>(T value, T lowerBound, T upperBound, ExclusionMode exclusionMode = ExclusionMode.None) where T : IComparable<T> 
    { 
     Contract.Ensures(((exclusionMode | ExclusionMode.LowerBound) == ExclusionMode.LowerBound ? value.CompareTo(lowerBound) > 0 : value.CompareTo(lowerBound) >= 0) && ((exclusionMode | ExclusionMode.UpperBound) == ExclusionMode.UpperBound ? value.CompareTo(upperBound) < 0 : value.CompareTo(upperBound) <= 0)); 
    } 
} 

我改變它到以下,它似乎工作,但顯然通用版本更可取。

[ContractVerification(false)] 
public static class Assert 
{ 
    [Conditional("DEBUG")] 
    public static void GreaterThan(int value, int lowerBound) 
    { 
     Contract.Ensures(value > lowerBound); 
    } 

    [Conditional("DEBUG")] 
    public static void GreaterThanOrEqual(int value, int lowerBound) 
    { 
     Contract.Ensures(value >= lowerBound); 
    } 

    [Conditional("DEBUG")] 
    public static void LessThan(int value, int upperBound) 
    { 
     Contract.Ensures(value < upperBound); 
    } 

    [Conditional("DEBUG")] 
    public static void LessThanOrEqual(int value, int upperBound) 
    { 
     Contract.Ensures(value <= upperBound); 
    } 

    [Conditional("DEBUG")] 
    public static void NotNull(object value) 
    { 
     Contract.Ensures(value != null); 
    } 

    [Conditional("DEBUG")] 
    public static void NotNullOrEmpty(string value) 
    { 
     Contract.Ensures(!string.IsNullOrEmpty(value)); 
    } 

    [Conditional("DEBUG")] 
    public static void True(bool value) 
    { 
     Contract.Ensures(value); 
    } 

    [Conditional("DEBUG")] 
    public static void False(bool value) 
    { 
     Contract.Ensures(!value); 
    } 

    [Conditional("DEBUG")] 
    public static void InRange(int value, int lowerBound, int upperBound, ExclusionMode exclusionMode = ExclusionMode.None) 
    { 
     Contract.Ensures(((exclusionMode | ExclusionMode.LowerBound) == ExclusionMode.LowerBound ? value > lowerBound : value >= lowerBound) && ((exclusionMode | ExclusionMode.UpperBound) == ExclusionMode.UpperBound ? value < upperBound : value <= upperBound)); 
    } 
} 

我只是想說明,即使不是一個解決方案,它具有CodeContracts沒有直接的源代碼,但在IL操作呢?

回答

2

你想要的是完全可能的,但並不是很多人知道它。首先,請將您的計算機上傳至C:\Program Files (x86)\Microsoft\Contracts\Languages\CSharp,並在您的項目中包含ContractExtensions.cs。它包含您需要的一些屬性。

然後,將ContractAbbreviator屬性應用於您的方法。您可以刪除[Conditional("DEBUG")][ContractVerification(false)]屬性,因爲您可以在項目的代碼合同屬性頁面中設置調試和發佈的合同行爲。請注意,您必須在方法開始時調用您的合約方法,否則您會在其中寫合同。你不能在方法中放置任何其他代碼。

public static class Assert 
{ 
    [ContractAbbreviator] 
    public static void GreaterThan<T>(T value, T lowerBound) 
     where T : IComparable<T> 
    { 
     Contract.Ensures(value.CompareTo(lowerBound) > 0); 
    } 

    // ... 
} 

雖然這是您的問題的答案,但它可能無法解決您的問題。原因是當a.CompareTo(b) > 0a > b成立時,靜態檢查器無法看到。所以,這個例子不會與你的仿製版本工作,但將與您的非通用版本的工作:

static int PlusOne(int value) 
{ 
    #region Contract 
    Contract.Requires(value > 0); 
    Assert.GreaterThan(value, 0); 
    #endregion 
    return value + 1; 
} 

編輯:

顯然我完全Assert類誤解了你的意圖。你的確可以做this forum上的建議。

但是,您不能指望靜態檢查器明白,例如,從x.CompareTo(y) > 0跟在x > y之後。原因?你可以把這些方法中的字面意思都加上。例如:

public int CompareTo(MyType t) 
{ 
    // Implementation not consistent with '>' 
    return this.field1 == t.field1 ? -1 : 1; 
} 

public static operator bool >(MyType left, MyType right) 
{ 
    // Implementation not consistent with CompareTo() 
    return left.CompareTo(right) <= 0; 
} 

您甚至可能沒有CompareTo。所以靜態檢查器無法看到它們之間的相似之處。

+0

我已經寫道它使用非通用版本,我知道這一點。自從CodeContracts發佈以來,我一直在與CodeContracts合作,並且我知道您的示例不會起作用,靜態檢查器會對這些斷言進行投訴,因爲ContractAbbreviator僅適用於需求,並且從未設計用於確保其他任何內容。在你的例子中,你在合同之後斷言是毫無意義的,斷言的重點是在被調用之後立即檢查另一個函數的輸出。 – 2012-07-30 21:33:55

+0

你的例子工作,因爲靜態檢查器已經驗證輸入,中間沒有函數返回需要斷言的任意值。我不是要求解決方案,而是要求此人告訴我爲什麼通用版本不起作用?爲什麼不能看到它?有什麼限制? – 2012-07-30 21:47:14

+0

你看到這個錯誤。 'ContractAbbreviator'可以同時處理_requires_和_ensures_。只有使用'ContractAbbreviator'屬性才能將方法調用所在的'GreaterThan'中的合約插入。這不是斷言(但你稱它爲'Assert'),它並不是合約之後。這是合同的一部分。在我的例子中'PlusOne'有一個_requires_和一個_ensures_(通過'GreaterThan')。最終,「GreaterThan」永遠不會被調用,只有合約被複制。我告訴過你爲什麼檢查器不能同時使用'CompareTo'和比較運算符。 – Virtlink 2012-07-30 23:25:14