2013-03-18 34 views
0

這是一個很好的結構候選人嗎?結構與字符串成員?無效的默認實例可接受?

考慮這樣的構造驗證輸入並存儲驗證的數據作爲一個單一的「路由代碼」這個永恆不變的結構例子:

struct RoutingCode 
{ 
    private readonly string routingCode; 

    RoutingCode(string prefix, string service, string sender) 
    { 
    // Validate prefix, service, sender strings 
    // ... 

    if (isInvalid) throw ArgumentException(); 

    // 
    this.routingCode = prefix + service + sender; 
    } 

    // Gets the validated routing code. 
    public string Routing 
    { 
    return this.routingCode; 
    } 

    public int RoutingLength 
    { 
    return this.routingCode.Length; 
    } 
} 

這個簡單的例子,在我看來是使用struct,而不是一個很好的候選的一個class

  • 它是不可變的。
  • 它代表一個奇異值。
  • 實例大小很小。

問題是所有的結構都有一個隱含的默認構造函數。在此示例中,默認構造函數RoutingCode()將實例化一個對象,其中Routing屬性返回null —這是一個無效的路由代碼。這與其他結構不同,例如PointBigInteger,其中支持字段對於默認實例包含非常合理且有效的「零」。

除了不確保有效的路由代碼,RoutingLength屬性拋出一個NullReferenceException如果在默認實例上調用。

保持這個struct與使它成爲class有什麼參數?

+0

我看到http://stackoverflow.com/q/13386719/733805基本上是重複的,接受的答案是使用'class'的好例子。所有的輸入是讚賞。 – 2013-03-18 04:18:02

+0

然而我同意你對重複使用案例的看法。然而,我並不完全同意這個結論,因爲你的用例對結構來說非常有用。最後,你必須權衡利弊... – bigge 2013-03-18 04:26:29

+0

@ bigge謝謝。我認爲「一個有效的默認實例」反對我最初使用struct的願望。由於'string'是一個引用類型,因此這裏的結構在效率方面並沒有太大的作用。 – 2013-03-18 04:38:16

回答

2

可以輕鬆解決您的默認值的問題:

struct RoutingCode 
{ 
    private readonly string routingCode;  
    RoutingCode(string prefix, string service, string sender) 
    { 
    // Validate prefix, service, sender strings 
    // ... 
    if (isInvalid) throw ArgumentException(); 
    this.routingCode = prefix + service + sender; 
    } 

    public string IsValid 
    { 
    get { return this.routingCode != null; } 
    } 

    // Gets the validated routing code. 
    public string Routing 
    { 
    get { return this.routingCode; } 
    } 

    public int RoutingLength 
    { 
    get { return this.routingCode == null ? 0 : this.routingCode.Length; } 
    } 
} 

OK,現在沒有任何一個屬性拋出異常,你有辦法告訴如果值是無效的。現在的問題是這是否是一個結構的好候選者。你是對的,它是(1)不變的,(2)小的,(3)邏輯上的一個值。如果你願意忍受可以表示無效值的事實,那麼這可能是結構的一個好候選。

一個更好的問題是:有沒有一個很好的理由,這不是一個類?而不是尋找反對使它成爲一個結構,尋找反對使它成爲一個類。

+0

答覆:「不要尋求反對使它成爲一個結構,尋找反對使它成爲一個類。」良好的建議,但探討相反是幫助突出兩者之間的差異,這是有用的。 – 2013-03-18 04:44:07

+0

我認爲(做出一兩個假設)Eric所說的是「你爲什麼要考慮將它改爲結構?」如果他不是,我會:什麼情況導致你看這段代碼並說「這應該是一個結構」? – JerKimball 2013-03-18 04:47:12

+0

@ KevinP.Rice:我絕對同意你的觀點,即所有的值都是有效值,對於結構來說是一個很好的屬性。這對於具有某種「安全」目的的結構尤爲重要;如果你有一個代表授予一些權利的「標記」的結構,你最好確定構造該結構默認值的能力不會打開安全漏洞。 – 2013-03-18 04:48:46

0

隱藏字段結構有可能讓未初始化的實例表現得好像它們包含任何特定的期望值[對於給定類型的所有未初始化的實例,默認值必須相同]。只需讓每個屬性訪問器或方法檢查類型是否保存系統默認值,如果是,則替換所需的默認值。

如果有人想允許代碼來創建這樣的情況:Routingnull並沒有這樣的情況下假設Routing是默認值,一個可以有類型聲明靜態字符串爲一個新的GUID(或別的東西,它是不會在別處出現),然後修改構造函數和get方法:

static string stringRepresentingNull = Guid.NewGuid().ToString(); 

RoutingCode(string wholeString) 
{ 
    if (wholeString == null) 
    wholeString = stringRepresentingNull; 
    this.routingCode = wholeString; 
} 

public string Routing 
{ 
    get { 
    if (this.routingCode == null) 
     return "12-3456-789"; // Default routing string 
    else if (Object.ReferenceEquals(routingCode,stringRepresentingNull) 
     return null; 
    else 
     return routingCode; 
} 

請注意,即使外部的代碼以某種方式設法猜測與該類型相關聯的GUID,有沒有辦法外代碼來生成一個字符串,其中ReferenceEquals將匹配stringRepresentingNull

在類似的情況下,如果有人想擁有一個默認值爲123的int屬性,則可以存儲backingField=(desiredValue^123)並擁有屬性獲取器收益率backingField^123

類型應該是結構還是類的問題很大程度上歸結爲是否希望將該類型的默認值表現爲null或者作爲有效值。如果你想要一個空默認值,使用一個類。如果你想要一個有效的默認值,使用一個結構體。

+0

優秀的貢獻和想法。在你最後的句子中,你轉置了類/結構嗎?另外,我並不完全遵循'^ 123'的目的。這是對安全性的混淆嗎? – 2013-12-21 03:10:09

+0

@KevinP。Rice:'^ 123'的目的是提供一個簡單的可逆變換,以便默認字段值零將讀取爲123,寫入123將缺省值(零)存儲到該字段,並寫入任何其他值'foo '將在該字段中存儲一個值,該值在讀取時將被轉換回'foo'。人們可以使用一個函數,它將0變成123和123變成0,同時保持其他值不受影響,但是無條件地使用xor會更快更容易。 – supercat 2013-12-21 03:17:24