如何在C#中处理枚举0(CA1008讨论)
规则指定所有枚举都应该有一个名为如何在C#中处理枚举0(CA1008讨论),c#,enums,code-analysis,C#,Enums,Code Analysis,规则指定所有枚举都应该有一个名为Unknown的0值(我们这里不讨论标志)。我理解您希望防止未初始化的值自动获得意义的原因。假设我定义以下枚举: enum Gender { Male, Female } class Person { public string Name { get; set; } public Gender Gender { get; set; } } 这规定了每个人应该是男性或女性(现在我们不讨论性别)。如果我忘记设置Gender属性,那
Unknown
的0
值(我们这里不讨论标志)。我理解您希望防止未初始化的值自动获得意义的原因。假设我定义以下枚举:
enum Gender
{
Male,
Female
}
class Person
{
public string Name { get; set; }
public Gender Gender { get; set; }
}
这规定了每个人应该是男性或女性(现在我们不讨论性别)。如果我忘记设置Gender
属性,那么此人将自动成为男性,这可能会导致问题。因此,我理解CA1008警告,因此0值应该保留给未知/未初始化的值
因此,让我们将Gender
枚举更改为,不再使用0值:
enum Gender
{
Male = 1,
Female = 2
}
如果我没有指定性别,那么这个人就不是男性或女性。序列化过程中可能会出现问题。值0
对于调试期间的枚举不是很有描述性。为了修复它并避免CA1008警告,我再次更改枚举:
enum Gender
{
Unknown = 0,
Male = 1,
Female = 2
}
未初始化的属性现在显示为Unknown
,看起来不错。但我可能引入了另一个问题,那就是未知
值看起来像一个有效值,可以应用于用户。我还可能收到关于不处理所有枚举值的警告。假设我使用的构造函数要求我指定性别和名称,以避免未初始化的属性:
public Person(string name, Gender gender)
{
Name = name ?? throw new ArgumentNullException(name);
Gender = gender;
}
当我定义Unknown
枚举时,现在可以显式地将性别设置为Unknown
。当然,这可以在构造函数中检查,但这只会在运行时发出信号。如果未定义未知
值,则调用方只能将其设置为男性或女性
修复方法可能是使用可为空的性别属性,因此未初始化的值是显式的null
值(我们不再定义Unknown
值)。但是使用可空类型会使编程更加复杂,所以我不建议这样做
将obsoletAttribute
应用于Unknown
值可能是个好主意。当有人显式使用该值时,会将其标记为警告(在构建时)
处理未初始化枚举值的正确方法是什么?使用obsoletateAttribute
是一个好主意还是有其他缺点
注:
*虽然这里的“过时”不是正确的语义,但如果使用了该值,它是生成警告的唯一(简单)方法。
*在没有默认构造函数的情况下使用POCO可能会使序列化复杂化,因此通常不建议在没有它们的情况下使用(可序列化的)类。枚举的目标是提供表示可能值的命名常量。在您的特定设计中(这是对现实世界的抽象),一个人的性别要么是
男性
,要么是女性
。没有None
您的枚举需要一个0
值成员,因为默认的基础类型是int
。出于这个原因(正如编译器所指出的),它应该是您的选择之一(男性
或女性
):
或
或
需要值这一事实应该由抽象的构造函数强制执行,因为人必须有性别:
public Person(string name, Gender gender)
{
Name = name ?? throw new ArgumentNullException(name);
Gender = gender;
}
另一方面,如果您的设计抽象出一个人可以选择而不是提供其性别的世界,那么您可以使用默认值表示:
public enum Gender
{
NotProvided, //compiler defaults to 0
Male,
Female
}
或
在这种情况下,有两种类型的构造函数是有意义的:
public Person(string name)
{
Name = name ?? throw new ArgumentNullException(name);
}
public Person(string name, Gender gender)
{
Name = name ?? throw new ArgumentNullException(name);
Gender = gender;
}
换句话说,编译器已经指示了正确的处理。您只需要确保您的抽象实现方式能够正确地表示所建模的内容。您的问题定义了三个我认为基本上互不兼容的需求:
enum性别
{
男,,
女性
}
抽象类人
{
公共字符串名称{get;set;}
公开摘要性别{get;}
}
类别:人
{
公共覆盖性别性别{get{return-Gender.Male;}
公众罪犯()
{ ... }
}
类女性:人
{
公共覆盖性别性别{get{return-Gender.Female;}
公共女性人士()
{ ... }
}
通过这种方式,您强制用户必须使用男性默认构造函数或女性默认构造函数实例化一个人。
序列化还能够保留子类型并使用默认构造函数,而不会导致错误的默认值
正在使用“过时”属性
public Person(string name, Gender gender)
{
Name = name ?? throw new ArgumentNullException(name);
Gender = gender;
}
public enum Gender
{
NotProvided, //compiler defaults to 0
Male,
Female
}
public enum Gender
{
NotProvided = 0
Male = 1,
Female = 2
}
public Person(string name)
{
Name = name ?? throw new ArgumentNullException(name);
}
public Person(string name, Gender gender)
{
Name = name ?? throw new ArgumentNullException(name);
Gender = gender;
}
abstract class Gender :
whatever interfaces you need for serialization and so on
{
private Gender() { } // prevent subclassing
private class MaleGender : Gender
{
// Serialization code for male gender
}
public static readonly Gender Male = new MaleGender();
// now do it all again for FemaleGender
}