使用Named和optional参数来区分构造函数

时间:2014-09-25 16:18:03

标签: c# constructor optional-parameters named-parameters

想象一个类,原则上可以通过指定两个属性之一的值来创建,这两个属性碰巧具有相同的类型。以下代码通过使用named and optional parameters的组合来区分两个构造函数来实现此目的:

class Program
{
    static void Main()
    {           
        //Note: these 2 ctors are distinguished only by the argument naming convention:
        thing thingWithMass = new thing(mass: 10);
        thing thingWithVolume = new thing(vol: 25);
    }

    class thing
    {
        int Density = 3;
        int Mass;
        int Vol;
        public thing(int mass)
        {
            Mass = mass;
            Vol = Mass/Density;
        }

        // Note the use of the optional variable to distinguish this ctor:
        public thing(int vol, bool isVol=true)
        {
            Vol = vol;
            Mass = Vol * Density;
        }
    }
}

所以(有点令人惊讶)这段代码编译并完美运行,但它是糟糕的形式吗?这看起来有点像诡计,我想知道是否存在一种对我来说不明显的潜伏危险?闻到了吗?

注意:在这种特殊情况下,我意识到我可以用一个看起来像这样的构造函数来完成同样的事情:

public thing(int mass=0, int vol=0) //plus a couple of if() statements in the body

但是在我的实际情况中涉及到很多其他参数,并且将它们全部组合到一个构造函数中会变得有点笨拙且难以阅读。

2 个答案:

答案 0 :(得分:3)

如果您的类有许多构造函数具有非常不同的逻辑和冲突类型的参数,请考虑使用静态工厂方法:

public static Thing CreateFromMass(int mass)
{
    return new Thing(mass, 0);
}

public static Thing CreateFromVol(int vol)
{
    return new Thing(0, vol);
}

如果你使用像这样的工厂方法,你可以使你的构造函数非公开。

不建议尽可能区分基于参数名称的构造函数,因为它在C#中非常罕见。请注意,您还必须使用带有可选参数的技巧来实现这一点 - 这是一个明确的指示,表明您做错了。

答案 1 :(得分:1)

IMO有点气味。如果消费者致电thing(10, false),该怎么办?这具有使用错误值创建thing的意外后果。

我可以想到两种可能的解决方案

1)使用Athari所描述的工厂。

2)为Mass和Volume创建类型。例如,

class Mass 
{
    private readonly int _mass;
    public Mass(int mass) { _mass = mass; }
    public int Value { get { return _mass; } } 
}

class Volume
{
    private readonly int _volume;
    public Mass(int volume) { _volume = volume; }
    public int Value { get { return _volume; } } 
}

然后,您可以将签名更改为

thing(Volume volume)
thing(Mass mass)

在回复您对不使用第二种方法的简单算术运算的评论时,您可以为intMass Volume定义隐式转换。

abstract class Number
{
    public static implicit operator int(Number number)
    {
        return number.Value;
    }

    public abstract  int Value { get; set; }
}

internal class Mass : Number
{
    public override int Value { get; set; }
    public static implicit operator Mass(int val) { return new Mass(){ Value = val }; }
}

internal class Volume : Number
{
    public static implicit operator Volume(int val) { return new Volume(){ Value = val }; }
    public override int Value { get; set; }
}

var mass = new Mass { Value = 10 };
var volume = new Volume { Value = 20 };
int product = mass * volume; // should work
mass = 10 * 20; // should also work