這裡已經存在第一個問題:當我們聲明一個類時,更多的是定義一系列相關的操作(或者叫行為、方法 ),當然類中也會包含字段和屬性,但這些字段通常都是為類的方法所使用,而屬性則常用於表示類的狀 態(比如StringBuilder的Length),類的能力(比如StringBuilder的 Capacity),方法進行的狀態或者階 段。而定義一個結構時,我們通常僅僅是用它來保存數據,而不提供方法,或者是僅提供對其自身進行操 作或者轉換的方法,而非對其它類型提供服務的方法。
Address 不包含任何的方法,它僅僅是將Provice、City、Zip這樣的三個數據組織起來成為一個獨立 的個體,所以最好將其聲明為一個Struct而非是一個Class。(這裡也有例外的情況:如果Address包含二 十個或者更多的字段,則考慮將其聲明為Class,因為Class在參數傳遞時是傳引用,而Struct是傳值。在 數據較小的情況下,傳值的效率更高一些;而在數據較大的時候,傳引用占據更小的內存空間。)
所以我們首先可以將Address聲明為一個Struct而非Class。
數據不一致的問題
我們接下來使用一下剛剛創建的Address類型:
Address a = new Address();
a.Province = "陝西";
a.City = "西安";
a.Zip = "710068";
Console.WriteLine(a.ToString()); // Province: 陝西, City: 西安, Zip: 710068
看上去是沒有問題的,但是回想下類型的定義,在給Zip屬性賦值時是有可能拋出異常的,所以我們還 是把它放在一個Try Catch語句中,同時我們給Zip賦一個錯誤的值,看會發生什麼:
try {
a.City = "青島";
a.Zip = "12345"; // 這裡觸發異常
a.Province = "山東";
} catch {
}
Console.WriteLine(a.ToString());//Province: 陝西, City: 青島, Zip: 710068
結果是出現了數據不一致的問題,當為Zip賦值的時候,因為引發了異常,所以對Zip以及其後的 Province的賦值都失敗了,但是對City的賦值是成功的。結果就是出現了Provice是陝西,City卻是青島 這種情況。
即是在賦值Zip時沒有引發異常,也會出現問題:在多線程情況下,當當前線程執行到修改了 City為 “青島”,但還沒有修改 Zip 和 Province的時候(Zip仍為 “710068”、Province仍為“陝西”)。如果 此時其他線程訪問類型實例a,那麼也將會讀取到不一致的數據。
常量性和原子性
我們現在已經知道了上面存在的問題,那麼接下來該如何改進呢?我們先來看看作者對常量性和原子 性給的定義:
對象的原子性:對象的狀態是一個整體,如果一個字段改變,其他字段也要同時做出相應改變。簡單 來說,就是要麼不改,要麼全改。
對象的常量性:對象的狀態一旦確定,就不能再次更改了。如果想再次更改,需要重新構造一個對象