4

(int数量、10進数の価格)または「4 /$3.99」を含む文字列のいずれかを受け入れるPriceオブジェクトがあるとします。一緒に設定できるプロパティを制限する方法はありますか?以下の私の論理で私を修正してください。

テスト:AとBは互いに等しいですが、Cの例は許可されるべきではありません。したがって、Cの例のように、3つのパラメータすべてが呼び出されないように強制するにはどうすればよいですか?

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(); } }
}
4

5 に答える 5

11

うーん....コンパイラと戦う代わりに、APIを再考する必要があるかもしれません。次のことを考慮しましたか。

  • セッターはいません。クラスは不変である必要があります。これにより、クラスはコンストラクターを介して完全に初期化され、無効な状態で初期化されることはありません。

  • セッターを主張する場合は、PriceTextプロパティを読み取り専用にし、他のプロパティを読み取り/書き込みすることができます。少なくともこれを行う際には、そのプロパティに渡されたテキストを検証する必要はありません。

  • たぶん、PriceTextプロパティを完全に削除し、.ToStringメソッドでオブジェクトの文字列表現をオーバーライドします。

最後のオプションは、私の意見では最良のアプローチです。解析と検証が必要なため、ユーザーが疑似シリアル化された文字列をクラスに渡す必要はないと思います。クラス自体よりも、クラスを使用するクライアントに負担がかかるはずです。

于 2010-05-29T15:46:36.190 に答える
5

プロパティのセッターをプライベートにし、メソッドを追加して価格を変更するか、メソッドを使用しない場合でも、新しいオブジェクトをインスタンス化するときにのみ価格が設定されるようにするのはどうでしょうか。

于 2010-05-29T15:40:53.953 に答える
5

Price文字列部分については、文字列を解析してインスタンスを返す静的メソッドがあると便利です。

例えば

Price newPrice = Price.FromString("4/$3.99");
Console.WriteLine("{0} qty for {1}", newPrice.Quantity, newPrice.Price);

これFromStringが静的メソッドです。これは、例を見たい場合
と同じです。Enum.Parse

于 2010-05-29T15:44:00.203 に答える
2

実行しているようにプロパティに直接値を割り当てることができ、デフォルトのコンストラクターがある場合、なぜコンストラクターを気にする必要がありますか?

priceTextおよびprice/の両方を外部で変更可能にする必要がある場合、qtyどのような条件下でオブジェクトの初期化が完了したと見なしますか?-

AdPrice C = new AdPrice();
C.qty = 4;
C.priceText = "2/$1.99";
C.price = 3.99m

上記は、「シンタティックシュガー」なしで使用しているものと同じコードです。あなたの例が起こらないようにするために、あなたは上記を防ぐことができる必要があるでしょう。

私の提案は、priceTextプロパティをプライベートにすることです。文字列を使用してオブジェクトを初期化するには、適切なコンストラクターを使用する必要があります。

于 2010-05-29T15:51:54.347 に答える
2

プロパティセッターをプライベートにし、2つのコンストラクターを提供することをお勧めします。1つは数量と価格を受け入れ、もう1つはテキスト表現を受け入れます。私の意見では、価格タイプには値タイプのセマンティクスがあるため、不変である必要があります。

さらに、オブジェクト初期化子はひどく酷使されていると思います。コンストラクターを呼び出した後、オブジェクトは完全に初期化され、すべての不変条件を満たす一貫した状態になっている必要があります。適切に設計されたコンストラクターのセットを提供することでこれを強制できますが、通常、デフォルトのコンストラクターを提供し、オブジェクト初期化子の使用に依存することでこれを強制することはできません。コンシューマーは、デフォルトのコンストラクターを呼び出すだけで、他には何もしません。

var employee = new Employee { Id = 42, FirstName = "John", LastName = "Doe" };

私の意見では、これは本当に悪いデザインです。次のコードのセマンティクスは何ですか?

var employee = new Employee();

これは、属性のない単なるオブジェクトです。使い物にならない。デフォルトのコンストラクターを提供しないことで、従業員が少なくともIDを持っていることを強制する方がはるかに良いと思います。すべての従業員インスタンスが他のプロパティの値を必要とするかどうかは実際のコンテキストによって異なりますが、このプロパティがある場合はもちろんコンストラクター引数になるはずです。

var employee = new Employee(42);
于 2010-05-29T15:54:43.830 に答える