2011-11-27 79 views
6

我想強制執行一個結構,使其始終對構造函數強制實施的某個合同有效。但合同受到default運營商的侵犯。如何在結構中執行合同

考慮下面的,例如:

struct NonNullInteger 
{ 
    private readonly int _value; 

    public int Value 
    { 
     get { return _value; } 
    } 

    public NonNullInteger(int value) 
    { 
     if (value == 0) 
     { 
      throw new ArgumentOutOfRangeException("value"); 
     } 

     _value = value; 
    } 
} 

// Somewhere else: 
var i = new NonNullInteger(0); // Will throw, contract respected 
var j = default(NonNullInteger); // Will not throw, contract broken 

作爲一種變通方法,我改變了結構的一類,所以我可以保證構造函數總是被調用初始化一個新的實例時。但我想知道,是否有絕對沒有辦法獲得與結構相同的行爲?

回答

3

一種方法是安排你的事情,這樣的默認值滿足合同:

struct NonNullInteger 
{ 
    private readonly int _valueMinusOne; 

    public int Value 
    { 
     get { return _valueMinusOne + 1; } 
    } 

    public NonNullInteger(int value) 
    { 
     if (value == 0) 
     { 
      throw new ArgumentOutOfRangeException("value"); 
     } 

     _valueMinusOne = value - 1; 
    } 
} 
+0

這實際上是滿足合同信件的一個很好的解決方案,儘管它也需要檢查int.MinValue。我不建議創建這個結構,但是,如果你真的想這樣做,這可能是最乾淨的解決方案。 –

+0

+1智能技巧。但是,對於更復雜的合同可能會更困難。 –

1

在這種情況下,由於默認狀態是無效的,因此最好使用不可變類。就內存使用情況而言,它的成本更高一些,但除非使用大量的內存,否則這應該不重要。實際上,對於每種方法而言,「非零數字」約束可能更好地處於合同級別,而不是放入類中。

如果您確實想要強制執行合同,請將該異常置於Value getter中而不是構造函數中。那麼合約就是你會拋出一個異常,如果它包含0值的話;這裏唯一真正的好處是你永遠不會默默使用零值。缺點是現在每次使用該值時都會進行比較。

1

雖然你不能達到你願意,你可以在吸氣驗證到底是什麼:

public int Value 
{ 
    get 
    { 
     if (_value == 0) 
      throw new InvalidOperationException("Use of uninitialized struct"); 
     return _value; 
    } 
}