【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 的枚举值与未设定任何标志或开启任何选项的状态关联起来

posted @ 2020-10-25 18:22  YanyuWu  阅读(183)  评论(0编辑  收藏  举报