[英]C#: Object having two constructors: how to limit which properties are set together?
假設您有一個Price對象接受(int數量,小數價格)或包含“4 / $ 3.99”的字符串。 有沒有辦法限制哪些屬性可以設置在一起? 請在下面的邏輯中隨意糾正我。
測試:A和B彼此相等,但不允許使用C示例。 因此,問題如何強制所有三個參數都不會像在C示例中那樣被調用?
AdPrice A = new AdPrice { priceText = "4/$3.99"}; // Valid
AdPrice B = new AdPrice { qty = 4, price = 3.99m}; // Valid
AdPrice C = new AdPrice { qty = 4, priceText = "2/$1.99", price = 3.99m};// Not
班級:
public class AdPrice {
private int _qty;
private decimal _price;
private string _priceText;
構造函數:
public AdPrice () : this( qty: 0, price: 0.0m) {} // Default Constructor
public AdPrice (int qty = 0, decimal price = 0.0m) { // Numbers only
this.qty = qty;
this.price = price; }
public AdPrice (string priceText = "0/$0.00") { // String only
this.priceText = priceText; }
方法:
private void SetPriceValues() {
var matches = Regex.Match(_priceText,
@"^\s?((?<qty>\d+)\s?/)?\s?[$]?\s?(?<price>[0-9]?\.?[0-9]?[0-9]?)");
if( matches.Success) {
if (!Decimal.TryParse(matches.Groups["price"].Value,
out this._price))
this._price = 0.0m;
if (!Int32.TryParse(matches.Groups["qty"].Value,
out this._qty))
this._qty = (this._price > 0 ? 1 : 0);
else
if (this._price > 0 && this._qty == 0)
this._qty = 1;
} }
private void SetPriceString() {
this._priceText = (this._qty > 1 ?
this._qty.ToString() + '/' : "") +
String.Format("{0:C}",this.price);
}
訪問者:
public int qty {
get { return this._qty; }
set { this._qty = value; this.SetPriceString(); } }
public decimal price {
get { return this._price; }
set { this._price = value; this.SetPriceString(); } }
public string priceText {
get { return this._priceText; }
set { this._priceText = value; this.SetPriceValues(); } }
}
嗯...而不是打擊編譯器,也許你只需要重新考慮你的API。 你考慮過以下幾點:
沒有二傳手。 您的類應該是不可變的,因此它通過構造函數完全初始化,並且不能在無效狀態下初始化。
如果您堅持使用setter,那么您的PriceText屬性可以是只讀的,而其他屬性是可讀/寫的。 至少在執行此操作時,您無需驗證傳遞到該屬性的文本。
也許完全刪除PriceText屬性,覆蓋.ToString方法中對象的字符串表示形式。
在我看來,最后一個選項是最好的方法。 我不認為用戶應該將偽序列化字符串傳遞給您的類,因為它需要解析和驗證 - 並且負擔應該真正在使用您的類而不是您自己的類本身的客戶端上。
如何將屬性中的setter設置為private並添加方法來更改價格甚至沒有方法,這樣只需在實例化新對象時設置價格。
對於字符串部分,最好有一個靜態方法來解析字符串並返回Price
實例。
例如
Price newPrice = Price.FromString("4/$3.99");
Console.WriteLine("{0} qty for {1}", newPrice.Quantity, newPrice.Price);
這里, FromString
是一個靜態方法。
如果你想看一個例子,這與Enum.Parse
相同。
如果你只是能夠像你一樣直接為屬性賦值, 並且有一個默認的構造函數,為什么還要使用構造函數呢?
如果priceText
和price
/ qty
都需要外部可變,那么在什么條件下你會認為對象初始化是完整的: -
AdPrice C = new AdPrice();
C.qty = 4;
C.priceText = "2/$1.99";
C.price = 3.99m
以上是與沒有“合成糖”的情況相同的代碼。 為了防止您的示例發生,您需要能夠阻止上述情況。
我的建議是將priceText
屬性priceText
私有。 要使用字符串初始化對象,需要使用適當的構造函數。
我建議將屬性設置者設為私有,只提供兩個構造函數 - 一個接受數量和價格,一個接受文本表示。 在我看來,您的價格類型具有值類型語義,因此應該是不可變的。
此外,我認為對象初始化器嚴重過度使用。 在調用構造函數之后,對象應該完全初始化並處於滿足所有不變量的一致狀態。 您可以通過提供一組設計良好的構造函數來強制執行此操作,但通常無法通過提供默認構造函數並依賴於對象初始值設定項的使用來強制執行此操作。 消費者可以自由地調用默認構造函數而不執行任何其他操作。
var employee = new Employee { Id = 42, FirstName = "John", LastName = "Doe" };
在我看來,這是非常糟糕的設計。 以下代碼的語義是什么?
var employee = new Employee();
它只是一個沒有任何屬性的對象。 無用。 我相信通過不提供默認構造函數來強制執行員工至少具有id會更好。 如果每個雇員實例都需要其他屬性的值取決於實際上下文,但如果他們具有此屬性,則當然應該成為構造函數參數。
var employee = new Employee(42);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.