想象一个类,原则上可以通过指定两个属性之一的值来创建,这两个属性碰巧具有相同的类型。以下代码通过使用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
但是在我的实际情况中涉及到很多其他参数,并且将它们全部组合到一个构造函数中会变得有点笨拙且难以阅读。
答案 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)
在回复您对不使用第二种方法的简单算术运算的评论时,您可以为int
和Mass
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