Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C#: Object having two constructors: how to limit which properties are set together?

Say you have a Price object that accepts either an (int quantity, decimal price) or a string containing "4/$3.99". Is there a way to limit which properties can be set together? Feel free to correct me in my logic below.

The Test: A and B are equal to each other, but the C example should not be allowed. Thus the question How to enforce that all three parameters are not invoked as in the C example?

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

The class:

public class AdPrice {
    private int _qty;
    private decimal _price;
    private string _priceText;

The constructors:

    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; }

The Methods:

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

The Accessors:

    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(); } }
}
like image 591
Zachary Scott Avatar asked Nov 29 '22 04:11

Zachary Scott


1 Answers

Hmmmm.... instead of fighting the compiler, maybe you just need to rethink your API. Have you considered the following:

  • No setters. Your class should be immutable, so that its fully initialized through the constructor and can't ever be initialized in an invalid state.

  • If you insist on setters, then your PriceText property can be readonly while the others are read/write. At least in doing this, you don't need to validate text passed into that property.

  • Maybe remove the PriceText property entirely, override the string representation of your object in the .ToString method.

The last option is the best approach in my opinion. I don't think users should be passing in pseudo-serialized strings to your class, because it requires parsing and validating -- and the burden should really be on the client using your class than your class itself.

like image 162
Juliet Avatar answered Dec 05 '22 17:12

Juliet