4

假设您有一个 Price 对象,它接受(整数数量,十进制价格)或包含“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(); } }
}
4

5 回答 5

11

嗯....与其对抗编译器,也许您只需要重新考虑您的 API。您是否考虑过以下问题:

  • 没有二传手。您的类应该是不可变的,以便它通过构造函数完全初始化,并且永远不能在无效状态下初始化。

  • 如果您坚持使用 setter,那么您的 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

如果你只是能够像你正在做的那样直接为属性赋值并且有一个默认构造函数,为什么还要使用构造函数呢?

如果priceTextand price/ qtyboth 需要在外部是可变的,那么在什么条件下您会认为对象初始化是完整的:-

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

上面的代码与您使用的代码相同,只是没有“语法糖”。为了防止您的示例发生,您需要能够防止上述情况。

我的建议是将priceText财产私有化。要使用字符串初始化对象,需要使用适当的构造函数。

于 2010-05-29T15:51:54.347 回答
2

我建议将属性设置器设为私有,只提供两个构造函数——一个接受数量和价格,一个接受文本表示。在我看来,您的价格类型具有值类型语义,因此应该是不可变的。

此外,我相信对象初始值设定项被过度使用。调用构造函数后,对象应完全初始化并处于满足所有不变量的一致状态。您可以通过提供一组精心设计的构造函数来强制执行此操作,但您通常无法通过提供默认构造函数并依赖对象初始值设定项的使用来强制执行此操作。消费者可以自由地调用默认构造函数而不做其他任何事情。

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 回答