【more effective C#】改善 C# 代码的 50 个有效方法 - 确保 0 可以当成值类型的有效状态来使用
.net 系统的初始化机制会默认将所有的对象的初始值都设置为 0。你无法强迫其他开发者必须用 0 以外的值来初始化值类型的某个实例。 因此,
你所创建的值类型
必须能够应对初始值为 0 的情况。
- enum 枚举尤其需要注意。如果某个类型无法将 0 当做有效的枚举值来看待, 那么就不应该将其设计为 enum。 所有的 enum 都继承自 System.ValueType, 其中的枚举数是从 0 开始计算的。 不过, 你也可以手动指定每个枚举值所对应的整数。
public enum Planet
{
// Explicitly assign values
// Default starts at 0 otherwise.
Mercury = 1,
Venus = 2,
Earth = 3,
Mars = 4,
Juptier = 5,
Saturn = 6,
Uranus = 7,
Neptune = 8
}
然后这样定义两个实例:
public void Run()
{
Planet sphere = new Planet();
var anotherSphere = default(Planet);
Console.WriteLine(sphere);
Console.WriteLine(anotherSphere);
}
运行结果:
这并不是有效的枚举值。
因此你自己定义的 enum 类型必须能够把 0 当成有效的枚举值来用。 于是将上面的 Planet 重新设计一下,添加对于 0 的值。
public enum Planet
{
// Explicitly assign values
// Default starts at 0 otherwise.
None = 0, // for default value.
Mercury = 1,
Venus = 2,
Earth = 3,
Mars = 4,
Juptier = 5,
Saturn = 6,
Uranus = 7,
Neptune = 8
}
再次运行程序后, 此时程序所表现的行为和意义则更标准:
上面所写的只不过是为了设计出一个更规范的 enum, 但是对于使用这个 enum 的开发者, 他们会如何使用, 是我们无法控制的。 我们只能通过设计一个好的 enum 来表达出所有被要求和即将被要求实现的需求。
合理的利用好默认值 0, 它代表的意思可能是没有开启, 也可能就是简单的 None。
- 值类型中包含引用类型的情况。
假设我们有这样的结构:
public struct LogMessage
{
private int ErrLevel;
private string msg;
}
LogMessage message = new LogMessage();
那么 message 的 msg 则为 null。你没有办法强迫用户在构造 message 的时候必须把 msg 设置成 null 以外的引用。
我们可以利用属性机制把这个问题局限在 LogMessage 结构体之外, 不让它影响到外界。比如像下面这样:
public struct LogMessage
{
private int ErrLevel;
private string msg;
public string Msg
{
get => msg ?? string.Empty;
set => msg = value;
}
}
系统会把值类型的所有实例都初始化为 0, 而且你无法禁止用户创建这种内容全都为 0 的值类型实例。 因此, 应该让程序在遇到这种情况时能够进入某个较为合理的状态中。 有一种特殊的情况需要注意:
如果枚举类型的变量来表示某组标志或其选项的时候, 那么应该将值为 0 的枚举值与未设定任何标志或开启任何选项的状态关联起来
。