选择正确的构造函数

时间:2014-11-26 18:25:56

标签: c# constructor

我有两个类:Purchase(父级)和DiscountPurchase(子级,另外一个字段“discount”)。每个都有2个构造函数(有和没有参数)。我需要解析字符串并创建一个实例。我知道,我可以这样做:

string[] parameters = csvString.Split(';');

string productName = parameters[0];
decimal cost = decimal.Parse(parameters[1]);
int productCount = int.Parse(parameters[2]);

if (parameters.Length < 4)
{                   
    newPurchase = new Purchase(productName, cost, productCount);
}
else
{
    decimal discount = decimal.Parse(parameters[3]);
    newPurchase = new FixedDiscountPurchase(productName, cost, productCount, discount);
}

但也许有更优雅的方式:反思或其他什么?

4 个答案:

答案 0 :(得分:1)

最好重新设计Purchase课程以获得discount字段,然后执行以下操作:

string[] parameters = csvString.Split(';');

string productName = parameters[0];
decimal cost = decimal.Parse(parameters[1]);
int productCount = int.Parse(parameters[2]);
decimal discount = parameters.Length < 4 ? 0 : decimal.Parse(parameters[3]);

newPurchase = new Purchase(productName, cost, productCount, discount);

或者保留单独的类并对其进行更改,以便if处理是否存在discount,而不是是否存在第四个参数。这增加了&#34的逻辑之间的一些解耦;这些数据来自&#34;和#34;一旦我拥有它,我该怎么办?#34;这是一件好事。

string[] parameters = csvString.Split(';');

string productName = parameters[0];
decimal cost = decimal.Parse(parameters[1]);
int productCount = int.Parse(parameters[2]);
decimal discount = parameters.Length < 4 ? 0 : decimal.Parse(parameters[3]);

if (discount > 0)
{
    newPurchase = new FixedDiscountPurchase(productName, cost,
                                             productCount, discount);       
}
else
{
    newPurchase = new Purchase(productName, cost, productCount);
}

如果您需要在逻辑上区分未指定折扣和折扣之间的差异,则可以使用decimal?null(而非0)作为discount 0被指定。

答案 1 :(得分:1)

这是工厂模式的典型用例。您知道,您需要Purchase的实例 - 但您不知道确切的子类型。 (有关更为复杂和有用的示例,请参阅http://www.dotnetperls.com/factory和此处:http://msdn.microsoft.com/en-us/library/orm-9780596527730-01-05.aspx

OFC。这并没有避免你已经实现的逻辑 - 它只是帮助你不必反复重复自己,并将逻辑封装在一个工厂中,你可以随时随地使用。

从您的示例中,一个简单的工厂可能如下所示:

static class PurchaseFactory
{
    public Static Purchase BuildPurchase(String[] parameters){
       string productName = parameters[0];
       decimal cost = decimal.Parse(parameters[1]);
       int productCount = int.Parse(parameters[2]);

       if (parameters.Length < 4)
       {                   
           return new Purchase(productName, cost, productCount);
       }
       else
       {
           decimal discount = decimal.Parse(parameters[3]);
           return new FixedDiscountPurchase(productName, cost, productCount, discount);
       }
    }
}

因此,从代码中的任何位置开始,您只需要:

string[] parameters = csvString.Split(';');
Purchase p = PurchaseFactory.BuildPurchase(parameters);

//p is now  either "Purchase" or "FixedDiscountPurchase"

好吧,如果它只是关于这两个类而希望知道价格是否打折,或者没有(并计算最终价格) - 你可以侥幸逃脱包含折扣标志的Purchase类以及在任何情况下获取FinalPrice的方法:

public class Purchase
    {
        public Decimal Discount { get; set; }
        public Boolean Discounted { get; set; }
        public String Name { get; set; }
        public Decimal Price { get; set; }
        public Int32 Count { get; set; }

        public Decimal FinalPrice
        {
            get
            {
                if (!Discounted)
                    return Price;
                else
                    return Price - Discount;
            }
        }

        public Purchase (String csvString){
            string[] parameters = csvString.Split(';');

            Name = parameters[0];
            Price = decimal.Parse(parameters[1]);
            Count = int.Parse(parameters[2]);

            if (parameters.Length == 4)
            {
                Discount = decimal.Parse(parameters[3]);
                Discounted = true;
            }
        }
    }

用法:

Purchase p = new Purchase(stringInput);
MessageBox.Show(p.FinalPrice.ToString());

请务必参考purchase.FinalPrice,然后您不必注意价格是否实际打折。

答案 2 :(得分:0)

我认为每个人都在考虑这个问题。如果这是一个你正在创建的全新项目,那么除了PurchaseDiscountPurchase之外还有其他类型,那么dognose关于创建工厂的答案可能就是这样。< / p>

不必撕掉代码并做很多工作,但是可以采用扩展方法:

public static Purchase GetPurchaseObject(this string csvString)
{
    string[] parameters = csvString.Split(';');

    string productName = parameters[0];
    decimal cost = decimal.Parse(parameters[1]);
    int productCount = int.Parse(parameters[2]);

    if (parameters.Length < 4)
    {
        return new Purchase(productName, cost, productCount);
    }
    else
    {
        decimal discount = decimal.Parse(parameters[3]);
        return new DiscountPurchase(productName, cost, productCount, discount);
    }
}

然后要使用它,您需要做的就是从CSV字符串中调用扩展方法:

string csvString1 = "TestProduct;15.50;5";
string csvString2 = "TestProduct;15.50;5;0.25";

Purchase p1 = csvString1.GetPurchaseObject();
Purchase p2 = csvString2.GetPurchaseObject();

if (p1 is DiscountPurchase)
{
    Console.WriteLine("p1 is a DiscountPurchase item");
}
else
{
    Console.WriteLine("p1 a Purchase item");
}

if (p2 is DiscountPurchase)
{
    Console.WriteLine("p2 is a DiscountPurchase item");
}
else
{
    Console.WriteLine("p2 a Purchase item");
}

从输出中可以看出,p1p2是不同的对象!根据CSV字符串的内容,它将知道您需要哪种对象。此外,如果您不喜欢扩展方法,可以将逻辑移动到静态方法中,并将csv字符串作为参数传递给方法。

答案 3 :(得分:0)

以下是如何使用反射进行此操作的方法。它的工作原理是尝试使用正确数量的参数使用它看到的第一个构造函数。它搜索的类型列表是您提供的类型,以及在与该类型相同的程序集中直接从该类型继承的任何类型。

它非常脆弱,难以阅读和不灵活(例如,如果你在其构造函数中添加了另外一种带有三个参数的购买,它就不会正常工作),这会让你知道什么它喜欢用反射来工作。 我不建议你这样做。

static object CreateInstance(Type rootType, object[] args)
{
    var types = rootType.Assembly.GetTypes().Where(t =>
        t == rootType || t.BaseType == rootType).ToArray();
    return CreateInstance(types, args);
}
static object CreateInstance(Type[] types, object[] args)
{
    foreach (var type in types)
    {
        foreach (var ctor in type.GetConstructors())
        {
            var parameters = ctor.GetParameters();
            if (args.Length == parameters.Length)
            {
                var newArgs = args.Select((x, i) =>
                   Convert.ChangeType(x, parameters[i].ParameterType)).ToArray();
                return ctor.Invoke(newArgs);
            }
        }
    }
    return null;
}

// use like
var newPurchase = CreateInstance(typeof(Purchase), parameters); // or
var newPurchase = CreateInstance(
         new[] { typeof(Purchase), typeof(FixedDiscountPurchase) }, parameters);