2011-12-05 149 views
7

我有幾個關於代碼合同的問題及其使用的最佳實踐。比方說,我們有一個類,有幾個屬性(見下文的例子):代碼合同最佳實踐

class Class1 
{ 
    // Fields 
    private string _property1;  //Required for usage 
    private List<object> _property2; //Not required for usage 

    // Properties 
    public string Property1 
    { 
     get 
     { 
      return this._property1; 
     }    
     set 
     { 
      Contract.Requires(value != null); 
      this._property1 = value; 
     } 
    } 
    public List<object> Property2 
    { 
     get 
     { 
      return this._property2; 
     } 
     set 
     { 
      Contract.Requires(value != null); 
      this._property2 = value; 
     } 
    } 

    public Class1(string property1, List<object> property2) 
    { 
     Contract.Requires(property1 != null); 
     Contract.Requires(property2 != null); 

     this.Property1 = property1; 
     this.Property2 = property2; 
    } 

    public Class1(string property1) 
     : this(property1, new List<object>()) 
    { } 
} 

一些解釋什麼,我想實現:

(一)property1是必填字段。對於正常使用對象而言,property2並不是明確需要的。

我有以下問題:

  1. 應我甚至與合同property2麻煩;因爲property2不是必填字段,所以它應該有合同。在物業上籤訂合同2是否表明它實際上是正常使用物品所必需的;

  2. 儘管property2沒有明確要求,但沒有可能的原因爲null,因此在setter中定義了契約。不會在property2上定義合同會減少調用代碼中的空檢查嗎?這應該可以減少錯誤並提高代碼的可維護性 - 這個假設是否正確?

  3. 如果它是正確的,我如何確保調用代碼property2永遠不會爲空?我使用Contract.Invariant(property2!= null);或者在構造函數中使用Contract.Ensures(property2!= null),或者在Init()中使用Contract.Ensures(property2!= null),或者使用setter中的Contract.Ensures(property!= null) (即如果使用Contract.Ensures(property2!= null),它放在哪裏)?

如果問題看起來很簡單,我很抱歉。我只是在尋找關於這個問題的想法,以及你們認爲最好的做法。

+0

我與財產2合同爲您列出的理由達成一致。 –

回答

3

這是什麼,我會盡量推薦的合同:

class Class1 
    { 
     // Fields 
     private string _property1;  //Required for usage 
     private List<object> _property2; //Not required for usage 

     // Properties 
     public string Property1 
     { 
      get 
      { 
       Contract.Ensures(Contract.Result<string>() != null); 
       return this._property1; 
      } 
      set 
      { 
       Contract.Requires(value != null); 
       this._property1 = value; 
      } 
     } 

     public List<object> Property2 
     { 
      get 
      { 
       Contract.Ensures(Contract.Result<List<object>>() != null); 
       return this._property2; 
      } 
      set 
      { 
       Contract.Requires(value != null); 
       this._property2 = value; 
      } 
     } 

     public Class1(string property1, List<object> property2) 
     { 
      Contract.Requires(property1 != null); 
      Contract.Requires(property2 != null); 

      this.Property1 = property1; 
      this.Property2 = property2; 
     } 

     public Class1(string property1) 
      : this(property1, new List<object>()) 
     { 
      Contract.Requires(property1 != null); 
     } 

     [ContractInvariantMethod] 
     private void ContractInvariants() 
     { 
      Contract.Invariant(_property1 != null); 
      Contract.Invariant(_property2 != null); 
     } 
    } 

的屬性有他們的公共行爲契約與不變量會抓住你以後可能會引入任何錯誤,您添加邏輯到Class那可以修改字段值,從而違反公共合同。或者,如果字段可以只讀(並且移除了setter),則不需要不變量。

+0

我會同意二傳手中的ce。在閱讀代碼合同樣本中的文檔之後,看起來這就是開發人員所期望的。對於這些不變量,稍後會發現違規行爲。謝謝你,你真的幫助我理解這一點。 –

2

我認爲有很多在這裏個人喜好,但我的2美分...

1)我會和將可能受到誘惑,調整構造有property2作爲可選參數:

Class1(string property1, List<object> property2 = new List<object>())  
{  
    Contract.Requires(property1 != null);  
    Contract.Requires(property2 != null);  

    this.Property1 = property1;  
    this.Property2 = property2;  
} 

2)請參見3

3)我很樂意減少調用代碼null檢查之前,我個人更喜歡看到一個

Contract.Ensures(property2 != null) 

關於getter - 我見過VS11 CTP在定義的工具提示中顯示契約,所以能夠看到這個我然後知道我不需要檢查空值。我會保留Requires在setter上。

+0

非常好。謝謝 –

1

通常當你在一個對象中有一個列表時,你將有對象來照顧創建。因此,如果消費者希望添加到列表中,他們必須首先獲取它。根據這個類的用法,這可能是更好的路線。

class Class1 
{ 
    // Fields 
    private string _property1;  //Required for usage 
    private List<object> _property2 = new List<object>(); //Not required for usage 

    // Properties 
    public string Property1 
    { 
     get 
     { 
      return this._property1; 
     }    
     set 
     { 
      Contract.Requires(value != null); 
      this._property1 = value; 
     } 
    } 
    public List<object> Property2 
    { 
     get 
     { 
      return this._property2; 
     } 
     //We don't have a setter. 
    } 

    public Class1(string property1) 
    { 
     Contract.Requires(property1 != null); 

     this.Property1 = property1; 
    } 
} 
+0

感謝列表方面的信息。這是我的擔憂之一,這就是爲什麼我把它作爲我的非必需財產。我主要就如何處理「非必要」財產的合同尋求建議,無論它是否是一個清單。任何意見? –

+0

如果它不是'Collection',我會遵循塗抹的建議。通過在構造函數中創建一個默認值並要求它不爲null,它會告知API的使用者,如果它們提供了一個,它們應該提供一個非空值。 –

1

1/2:在property2上有一個契約是合適的,如果那是你想要執行的行爲,即它不應該爲null,並且肯定會減少null檢查和空值周圍潛在錯誤的需要。

3:要回答這個問題,我已經重寫你的類如下

class Class1 
{ 
    //Properties 
    public string Property1 { get; set; } 
    public List<object> Property2 { get; set; } 

    public Class1(string property1, List<object> property2) 
    { 
     Contract.Requires(property1 != null); 
     Contract.Requires(property2 != null); 

     Property1 = property1; 
     Property2 = property2; 
    } 

    public Class1(string property1) 
     : this(property1, new List<object>()) 
    { 
     Contract.Requires(property1 != null); 
    } 

    [ContractInvariantMethod] 
    private void ObjectInvariant() 
    { 
     Contract.Invariant(Property1 != null); 
     Contract.Invariant(Property2 != null); 
    } 
} 

當你在你的類自動實現的屬性,你可以使用Contract.Invariant()。這會使您的財產中的顯式合約變得冗餘,因此現在不需要以下代碼。

public string Property1 
{ 
    get 
    { 
     Contract.Ensures(Contract.Result<string>() != null); 
     return this._property1; 
    }    
    set 
    { 
     Contract.Requires(value != null); 
     this._property1 = value; 
    } 
} 

這將照顧財產保護。要充分確保該屬性永遠不會爲空,您將在構造函數中添加一個Contract.Requires(property1!= null)。

我知道這個答案遲了3年,但它可能對你有些用處!

來源:http://research.microsoft.com/en-us/projects/contracts/userdoc.pdf