有两种方法可以实现重载。第一个是在一个方法/构造函数中完成所有操作,并从其他重载中调用它,这会导致更长的方法体。第二个是在每个重载中执行最少的操作,因此有时代码难以导航并且难以理解哪个重载执行什么操作。
例如,如果一个类 Cat
的两个重载是:
public Cat(string name, int? weight, Color mainColor);
public Cat(string name);
有两种方法可以实现:
第一种类型
public Cat(string name, int? weight, Color mainColor)
{
// Initialize everything.
this.name = name;
if (weight.HasValue) this.weight = weight.Value;
// There is a bug here (see the anwer of @Timwi): mainColor can be null.
this.colors = new List<Colors>(new[] { mainColor });
}
public Cat(string name)
: this(name, null, null)
{
// Nothing else to do: everything is done in the overload.
}
第二类
public Cat(string name)
{
// Initialize the minimum.
this.name = name;
this.colors = new List<Colors>();
}
public Cat(string name, int? weight, Color mainColor)
: this(name)
{
// Do the remaining work, not done in the overload.
if (weight.HasValue) this.weight = weight.Value;
this.colors.Add(mainColor);
}
问题
- 如何调用这两种类型的重载(能够在互联网或书籍中查找更多信息)?
- 在这些类型之间进行选择时,需要考虑的主要问题/因素是什么?
注意:由于 C# 4.0 允许您指定可选参数,为了避免歧义,假设我只讨论 C# 3.0。
There are two ways to implement overloads. The first one is to do everything in one method/constructor and call it from other overloads, which leads to longer method bodies. The second one is to do the minimum in each overload, thus having a code sometimes difficult to navigate and to understand which overload does what.
For example, if two overloads of a class Cat
are:
public Cat(string name, int? weight, Color mainColor);
public Cat(string name);
there are two ways to implement this:
First type
public Cat(string name, int? weight, Color mainColor)
{
// Initialize everything.
this.name = name;
if (weight.HasValue) this.weight = weight.Value;
// There is a bug here (see the anwer of @Timwi): mainColor can be null.
this.colors = new List<Colors>(new[] { mainColor });
}
public Cat(string name)
: this(name, null, null)
{
// Nothing else to do: everything is done in the overload.
}
Second type
public Cat(string name)
{
// Initialize the minimum.
this.name = name;
this.colors = new List<Colors>();
}
public Cat(string name, int? weight, Color mainColor)
: this(name)
{
// Do the remaining work, not done in the overload.
if (weight.HasValue) this.weight = weight.Value;
this.colors.Add(mainColor);
}
Questions
- How those two types of overloads are called (to be able to look for further information on internet or in books)?
- What must be the major concerns/factors to take in account when choosing between those types?
Note: since C# 4.0 let you specify optional parameters, to avoid ambiguity, let's say I'm talking about C# 3.0 only.
发布评论
评论(2)
我认为这是另一个例子,没有一个单一的、教条式的答案能够合理地涵盖所有情况。我总是会考虑具体情况并根据所有可用因素做出决定。
一个因素是第一个有很多如果。您的代码还有一个错误:您将在颜色列表中添加一个
null
值;为了修复这个错误,你需要更多的假设。这样的构造函数很容易变得混乱。无数的 if 表明有几种情况的逻辑有很大不同,因此为每种情况使用单独的构造函数是非常有意义的。然而,在没有那么多 if 的情况下,所有的逻辑都是相同的,所以现在调用一个执行该逻辑的构造函数是有意义的,并且做得很好。那么就只有一个地方可以维护它。
另一个因素是,在您的示例中,第一个因素使
weight
未初始化。 这不一定是一件坏事,因为幸运的是 C# 中的默认初始化是可以预测的;但如果weight
的字段声明将其初始化为非零值,并且只有某些构造函数用另一个值覆盖该默认值,我会认为这种形式很糟糕。构造函数参数和/或this(...)
调用是记录该字段的默认值的更好位置。 (最好是构造函数参数,因为这样即使是客户端程序员也可以看到默认值,但显然这需要 C# 4。)当然,也可以初始化全部使用字段初始值设定项的字段并将构造函数保留为空,如果您只有一个不带参数的构造函数,这是一种合理的策略。所以,是的,就像您所说的,您不希望方法体变得太长,但您也不希望代码变得太难以导航,因此您需要针对任何给定情况在两者之间取得平衡。
I think this is another one of those examples where no single, dogmatic answer will reasonably cover all cases. I would always look at the individual case and decide based on all available factors.
One factor is that the first one has lots of ifs. Your code also has a bug: you would be adding a
null
value into the list of colors; in order to fix that bug, you need even more ifs. Such a constructor can easily get messy. A myriad of ifs is indicative that there are several cases in which the logic is substantially different, so having separate constructors for each of those cases makes perfect sense.However, in cases where there aren’t that many ifs, the logic is the same for all, so now it makes sense to call a single constructor that does that one logic, and does it well. Then there’s only one place to maintain it.
Another factor is that in your example, the first one leaves
weight
uninitialised. This need not be a bad thing because fortunately default initialisation in C# is predictable; but I would consider it bad form if the field declaration forweight
were to initialise it to something non-zero and only some of the constructors overwrite that default with another value. The constructor parameters and/or thethis(...)
call are better places to document the default value for that field. (Preferably the constructor parameters, because then even the client programmer can see the default value, but obviously that requires C# 4.) Of course, it is also possible to initialise all the fields using a field initialiser and leave the constructor(s) empty, which is a reasonable strategy if you only have one constructor with no arguments.So yeah, like you said, you don’t want method bodies to get too long, but you also don’t want the code to get too difficult to navigate, so you need to strike a balance between the two for any given situation.
第一个
构造函数重载必须始终相互调用或调用公共初始化方法。这是重构和代码模块化的问题,因此您只需进行一次更改。
例如,如果您想添加以下内容:
在第二种情况下,您必须在第二个情况的两个位置执行此操作,但在第一个情况的一个位置执行此操作。
同样按照惯例,对构造函数进行排序,使其首先以最少的参数开始。
First one
Constructor overload must always call each other or a common initializer method. It is a concern of refactoring and code modularity so that you would have to make a change only once.
For example if you want to add this:
In the second case you have to do it in two places for the second one but in one place in the first one.
Also per convention, order the constructors such that they start with the least parameters first.