学习 C#(第 6 天):理解 C# 中的枚举(实用方法)






4.89/5 (124投票s)
第 6 天:理解 C# 中的枚举。我“深入面向对象”系列文章将解释 C# 中的 enum 数据类型。
引言
我“深入面向对象”系列文章将解释 C# 中的 enum
数据类型。我们将通过动手实践来学习,而不仅仅是理论。我们将探索 enum
的强大功能,并涵盖几乎所有可以使用 enum
的场景。我们将遵循一种实用的学习方法来理解这个概念。我们可能会遇到复杂的示例来更深入地理解这个概念。
枚举(定义)
让我们从 MSDN 的定义开始:
“
enum
关键字用于声明一个枚举,它是一种独立的类型,由一组名为枚举数的命名常量组成。通常,最好将
enum
直接定义在命名空间内,以便命名空间中的所有类都可以方便地访问它。但是,enum
也可以嵌套在类或结构中。默认情况下,第一个枚举数为值
0
,并且每个连续枚举数的值增加 1。例如,在下面的枚举中,Sat 为 0,Sun 为 1,Mon 为 2,依此类推。”
图片来源:http://pixabay.com/en/job-interview-career-conference-156130/
先决条件
我希望我这篇博文的读者对 C# 有非常基础的了解,我一直希望我的读者在阅读本文时感到愉快。另外,请继续自己编写本文中提供的示例程序,以获得实践经验并更深入地理解概念。
路线图
让我们回顾一下我们的路线图。
图片来源:http://borgefagerli.com/at-a-crossroads/cross-roads/
- 深入OOP(第1天):多态性和继承(早期绑定/编译时多态)
- 深入OOP(第2天):多态性和继承(继承)
- 深入OOP(第3天):多态性和继承(动态绑定/运行时多态)
- 深入OOP(第4天):多态性和继承(C#中抽象类的一切)
- 深入OOP(第5天):C#中访问修饰符的一切(Public/Private/Protected/Internal/Sealed/Constants/Readonly字段)
- 学习 C#(第 6 天):理解 C# 中的枚举(实用方法)
- 学习 C# (第 7 天):C# 中的属性 (实用方法)
- 学习 C#(第 8 天):C# 中的索引器(实用方法)
- 学习 C#(第 9 天):理解 C# 中的事件(深入探讨)
- 学习C#(第10天):C#中的委托(一种实用方法)
- 学习C#(第11天):C#中的事件(一种实用方法)
实用方法
Enum
承担着与类几乎相同的责任,即创建一种新的数据类型,并且它与类、接口或结构处于同一级别。
只需打开 Visual Studio 并添加一个名为 Enum
s 的控制台应用程序。您将得到 Program.cs 类。
注意:本文中的每个代码片段都经过了尝试和测试。
在与 Program
类相同的级别声明一个 enum
,称之为 Color
。
Program.cs
namespace Enums
{
class Program
{
static void Main(string[] args)
{
}
}
enum Color
{
Yellow,
Blue,
Brown,
Green
}
}
在上述示例中,我们通过 enum
创建了一个新的 datatype
。datatype
是 Color
,它有四种不同的值:Yellow
、Blue
、Brown
和 Green
。我们在声明的 enum
中写的文本可以是您想要的任何内容,它只是为您提供了一个自定义的枚举列表。
按如下方式修改您的主程序:
using System;
namespace Enums
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(Color.Yellow);
Console.ReadLine();
}
}
enum Color
{
Yellow,
Blue,
Brown,
Green
}
}
运行程序。
输出:Yellow
现在,将 Color.Yellow
转换为 int
,我们会得到什么?
using System;
namespace Enums
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine((int)Color.Yellow);
Console.ReadLine();
}
}
enum Color
{
Yellow,
Blue,
Brown,
Green
}
}
输出:0
我们看到 enum
被称为 static
变量,因此这里的 enum
可以被视为 static
对象。因此,上面示例中的其他 enum
可以像 Yellow
一样声明,例如 Blue
可以声明为 Color.Blue
。我们在上面两个示例中看到的输出是转换后的 0
和未转换的 Yellow
,因此我们在这里看到它的行为非常类似于数组,其中 Yellow
的值为 0
,类似地,Blue
的值为 1
,Brown
:2
,Green
:3
。
因此,当我们执行 Color.Yellow
时,它就像显示数字 0
,所以从这里我们可以推断出 enum
代表一个常量数字,因此 enum
类型是一个具有命名常量的独立类型。
要记住的点:enum
代表常量数字,enum
类型被称为具有命名常量的独立类型。
底层数据类型
Program.cs
using System;
namespace Enums
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine((byte)Color.Yellow);
Console.WriteLine((byte)Color.Blue);
Console.ReadLine();
}
}
enum Color:byte
{
Yellow,
Blue,
Brown,
Green
}
}
输出
0
1
注意:本文中的每个代码片段都经过了尝试和测试。
我们所做的唯一改变是指定了我们声明的底层 enum
的类型。enum
的默认 datatype
是 int
,这里我们指定了数据类型为 byte
,并得到了结果。
enum
可以指定更多数据类型,如 long
、ulong
、short
、ushort
、int
、uint
、byte
和 sbyte
。
要记住的点:我们不能将 char
声明为 enum
对象的底层数据类型,因为 char
存储 Unicode 字符,而 enum
对象的 datatype
只能是数字。
枚举中的继承
Program.cs
using System;
namespace Enums
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine((byte)Color.Yellow);
Console.WriteLine((byte)Color.Blue);
Console.ReadLine();
}
}
enum Color:byte
{
Yellow,
Blue,
Brown,
Green
}
enum Shades:Color
{
}
}
输出
编译时错误:预期类型为 byte
、sbyte
、short
、ushort
、int
、uint
、long
或 ulong
。
我们在这里清楚地看到,enum
不能从除了错误中提到的类型之外的任何其他类型派生。
要记住的点:enum
不能从除了 byte
、sbyte
、short
、ushort
、int
、uint
、long
或 ulong
类型之外的任何其他类型派生。
让我们从 enum
派生一个类,称之为类 Derived
,因此我们的代码。
Program.cs
class Program
{
static void Main(string[] args)
{
Console.WriteLine((byte)Color.Yellow);
Console.WriteLine((byte)Color.Blue);
Console.ReadLine();
}
}
枚举
enum Color:byte
{
Yellow,
Blue,
Brown,
Green
}
Derived.cs
class Derived:Color
{
}
编译代码。
输出
编译时错误:“Enums.Derived”:不能从密封类型“Enums.Color”派生
图片来源:https://www.flickr.com/photos/lwr/931211869/
要记住的点:默认情况下,enum
是一个密封类,因此遵守密封类遵循的所有规则,所以任何类都不能从 enum
(即密封类型)派生。
System.Enum
可以是 enum
的基类吗?
Program.cs
using System;
namespace Enums
{
internal enum Color: System.Enum
{
Yellow,
Blue
}
internal class Program
{
private static void Main(string[] args)
{
}
}
}
输出
编译时错误:预期类型为 byte
、sbyte
、short
、ushort
、int
、uint
、long
或 ulong
。
要记住的点:enum
类型隐式派生自 System.Enum
,因此我们不能显式地从 System.Enum
派生它。
此外,enum
还派生自三个接口:IComparable
、IFormattable
和 IConvertible
。
A. IComparable
让我们检查一下:
Program.cs
using System;
namespace Enums
{
internal enum Color
{
Yellow,
Blue,
Green
}
internal class Program
{
private static void Main(string[] args)
{
Console.WriteLine(Color.Yellow.CompareTo(Color.Blue));
Console.WriteLine(Color.Blue.CompareTo(Color.Green));
Console.WriteLine(Color.Blue.CompareTo(Color.Yellow));
Console.WriteLine(Color.Green.CompareTo(Color.Green));
Console.ReadLine();
}
}
}
输出
-1
-1
1
0
有时,我们可能会遇到定义了大量 enum
的情况,并且我们想比较 enum
的值以检查它们是否小于、大于或等于彼此。
由于所有 enum
都隐式派生自实现了 IComparable
接口的 Enum
类,因此它们都有一个 CompareTo()
方法,我们在上面的示例中刚使用过。该方法是非 static
的,必须通过成员使用。Yellow 的值为 0,Blue 为 1,Green 为 2。在第一个语句中,当 Color.Yellow
与 Color.Blue
比较时,Yellow
的值小于 Blue
,因此返回 -1,第二个语句也一样,当 Color.Blue
与 Color.Green
比较时。Green
的值(即 2)大于 Color.Blue
的值(即 1)。在第三个语句中,即第一个语句的反向,我们得到比较结果 1,因为 Blue
大于 Yellow
。在最后一个语句中,当 Color.Green
与自身比较时,我们无疑得到值为 0。
因此,值 -1
表示值较小,1
表示值较大,0
表示 enum
成员的值相等。
另一个比较示例如下:
Program.cs
using System;
namespace Enums
{
enum Color
{
Yellow,
Blue,
Green
}
internal class Program
{
private static void Main(string[] args)
{
int myColor = 2;
if(myColor== Color.Green)
{
Console.WriteLine("my color");
}
Console.ReadLine();
}
}
}
输出
Compile time error : Operator '==' cannot be applied to operands of type 'int' and 'Enums.Color'
在上面的示例中,我们尝试将 int
类型与 Enum
类型进行比较,结果是编译时错误。由于 enum
充当独立的数据类型,因此不能直接与 int
进行比较。但是,我们可以将 enum
类型转换为 int
来执行比较,如下面的示例所示:
注意:本文中的每个代码片段都经过了尝试和测试。
Program.cs
using System;
namespace Enums
{
enum Color
{
Yellow,
Blue,
Green
}
internal class Program
{
private static void Main(string[] args)
{
int myColor = 2;
if(myColor== (int)Color.Green)
{
Console.WriteLine("my color");
}
Console.ReadLine();
}
}
}
输出:my color
B. IFormattable
Program.cs
using System;
namespace Enums
{
internal enum Color
{
Yellow,
Blue,
Green
}
internal class Program
{
private static void Main(string[] args)
{
System.Console.WriteLine(Color.Format(typeof(Color), Color.Green, "X"));
System.Console.WriteLine(Color.Format(typeof(Color), Color.Green, "d"));
Console.ReadLine();
}
}
}
输出
00000002
2
Format
是从 IFormatter
接口派生的方法。它是一个 static
方法,可以直接与声明为 Color
的 enum
类一起使用。它的第一个参数是 enum
类的类型,第二个是要格式化的成员,第三个是格式,即十六进制或十进制,就像我们在上面的示例中使用的一样,我们得到了一个正面的结果。
C. IConvertible
using System;
namespace Enums
{
enum Color
{
Yellow,
Blue,
Green
}
internal class Program
{
private static void Main(string[] args)
{
string[] names;
names = Color.GetNames(typeof (Color));
foreach (var name in names)
{
Console.WriteLine(name);
}
Console.ReadLine();
}
}
}
输出
Yellow
Blue
Green
注意:本文中的每个代码片段都经过了尝试和测试。
GetNames
是一个 static
方法,它接受 Type
(即类型实例)作为参数,并返回一个 string
数组。就像在上面的示例中一样,我们的 enum
中有 3 个成员的数组,因此它们的名字一个接一个地显示。
另一个例子如下:
Program.cs
using System;
namespace Enums
{
enum Color
{
Yellow,
Blue,
Green
}
internal class Program
{
private static void Main(string[] args)
{
Console.WriteLine(Color.Blue.ToString());
Console.WriteLine(Color.Green.ToString());
Console.ReadLine();
}
}
}
输出
Blue
Green
正如我们在上面的示例中看到的,我们将 enum
类型转换为字符串类型并得到了输出,因此,可以使用许多预定义的转换方法将 enum
从一种数据类型转换为另一种数据类型。
要记住的点:可以使用许多预定义的转换方法将 enum
从一种数据类型转换为另一种数据类型。
重复、默认值和初始化
Program.cs
using System;
namespace Enums
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine((byte)Color.Yellow);
Console.WriteLine((byte)Color.Blue);
Console.ReadLine();
}
}
enum Color
{
Yellow,
Blue,
Brown,
Green,
Blue
}
}
输出
Compile time error: The type 'Enums.Color' already contains a definition for 'Blue'
在上面的示例中,我们只是重复了 Color
的 enum
成员 Blue
,并且得到了编译时错误,因此我们现在知道 enum
不能包含两个具有相同名称的成员。默认情况下,如果未指定第一个值,则第一个成员的值为 0
,并将其递增 1 为后续成员。
再举一个例子。
Program.cs
using System;
namespace Enums
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine((int)Color.Yellow);
Console.WriteLine((int)Color.Blue);
Console.WriteLine((int)Color.Brown);
Console.WriteLine((int)Color.Green);
Console.ReadLine();
}
}
enum Color
{
Yellow =2,
Blue,
Brown=9,
Green,
}
}
输出
2
3
9
10
惊讶!我们总是可以为任何 enum
成员指定默认常量值,在这里我们看到,我们为 yellow
指定了值 2
,所以根据 enum
的定律,blue
的值将增加 1 并获得值 3
。我们再次为 Brown
指定 9
作为默认值,因此其后继 Green
的值将增加 1 并获得值 10
。
继续下一个例子。
Program.cs
using System;
namespace Enums
{
class Program
{
static void Main(string[] args)
{
}
}
enum Color:byte
{
Yellow =300 ,
Blue,
Brown=9,
Green,
}
}
输出
Compile time error: Constant value '300' cannot be converted to a 'byte'
我们刚刚从 byte
派生了我们的 enum
,我们知道我们可以这样做吗?然后我们将 yellow
的值从 2
更改为 300
,并得到了编译时错误。由于这里的底层数据类型是 byte
,因此很简单,我们不能为 enum
成员指定超出底层数据类型范围的值。值 300
超出了 byte
的范围。这类似于为字节数据类型变量赋值超出范围的值。
另一个例子
Program.cs
using System;
namespace Enums
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine((int)Color.Yellow);
Console.WriteLine((int)Color.Blue);
Console.WriteLine((int)Color.Brown);
Console.WriteLine((int)Color.Green);
Console.ReadLine();
}
}
enum Color
{
Yellow = 2,
Blue,
Brown = 9,
Green = Yellow
}
}
输出
2
3
9
2
这里我们将 Green
初始化为 Yellow
,并且没有收到任何错误,所以我们看到,可以为多个 enum
成员初始化相同的值。
要记住的点:可以为多个 enum
成员初始化相同的值。
Program.cs
using System;
namespace Enums
{
class Program
{
static void Main(string[] args)
{
Color.Yellow = 3;
}
}
enum Color
{
Yellow = 2,
Blue,
Brown = 9,
Green = Yellow
}
}
输出
Compile time error: The left-hand side of an assignment must be a variable, property or indexer
在上面的示例中,我们尝试在已定义的 enum
的范围之外(即在另一个类中)初始化 enum
成员,并得到了编译时错误。我们不能忘记 enum
充当常量,其值在初始化后不能改变。
要记住的点:enum
充当常量,因此其值在初始化后不能改变。
可读性
Program.cs
using System;
namespace Enums
{
internal enum Color
{
Yellow,
Blue,
Brown,
Green
}
internal class Program
{
private static void Main(string[] args)
{
Console.WriteLine(CheckColor(Color.Yellow));
Console.WriteLine(CheckColor(Color.Brown));
Console.WriteLine(CheckColor(Color.Green));
Console.ReadLine();
}
public static string CheckColor(Color color)
{
switch (color)
{
case Color.Yellow:
return "Yellow";
case Color.Blue:
return "Blue";
case Color.Brown:
return "Brown";
case Color.Green:
return "Green";
default:
return "no color";
}
}
}
}
输出
Yellow
Brown
Green
在这里,在上面的示例中,我们声明了一个 enum Color
,其中包含各种 color
成员。有一个名为 program
的类,其中包含一个名为 CheckColor
的 static
方法,该方法具有一个 switch
语句,根据传递给方法的参数(即 Enum Color
)来检查 color
。在 Main
方法中,我们尝试访问 CheckColor
方法,传递各种参数。我们看到 CheckColor
方法中的 switch
语句可以接受任何传递的 datatype
,并且返回的 case
语句使用该类型的名称而不是纯 int
数字来比较结果。我们看到这使我们的程序更具可读性。因此,enum
在使程序更具可读性和结构化、易于理解方面起着重要作用。
循环依赖
Program.cs
using System;
namespace Enums
{
internal enum Color
{
Yellow=Blue,
Blue
}
internal class Program
{
private static void Main(string[] args)
{
}
}
}
输出
Compile time error: The evaluation of the constant value for 'Enums.Color.Yellow' involves a circular definition
与常量一样,我们也无法在 enum
中出现循环依赖。我们将 Blue
的值赋给 Yellow
,而 Blue
又作为下一个 enum
成员递增 1,这导致 Blue
对 yellow
的循环依赖,并导致错误。C# 很聪明,能够捕获这种伎俩。
深入探讨
让我们看一些复杂的场景。
实验1
Program.cs
using System;
namespace Enums
{
enum Color
{
}
internal class Program
{
private static void Main(string[] args)
{
Color color = (Color) -1;
Console.ReadLine();
}
}
}
注意:本文中的每个代码片段都经过了尝试和测试。
输出
Compile time error:
To cast a negative value, you must enclose the value in parentheses
'Enums.Color' is a 'type' but is used like a 'variable'
在上面的示例中,我们正在将负值转换为 enum
,但编译器说,在转换负值时,我们必须将其放在括号中。这并不奇怪,因为 C# 知道“-”也是一元运算符,使用上述代码可能会导致编译器混淆,认为我们正在使用减法或将负值进行类型转换。因此,在进行负值类型转换时,请务必使用括号。
实验2
Program.cs
using System;
namespace Enums
{
enum Color
{
value__
}
internal class Program
{
private static void Main(string[] args)
{
}
}
}
输出
Compile time error: The enumerator name 'value__' is reserved and cannot be used
我们在这里清楚地看到,我们有 value__
作为枚举数的保留成员。C# 编译器喜欢这个关键字,它有大量的保留内置关键字。
图片来源:www.vector.rs
它可能会保留这个关键字来内部跟踪 enum
成员,但我不确定。
摘要
让我们回顾一下我们必须记住的所有要点。
enum
代表常量数字,enum
类型被称为具有命名常量的独立类型。- 我们不能将
char
声明为enum
对象的底层数据类型,因为char
存储 Unicode 字符,而enum
对象的datatype
只能是数字。 enum
不能从除了byte
、sbyte
、short
、ushort
、int
、uint
、long
或ulong
类型之外的任何其他类型派生。- 默认情况下,
enum
是一个密封类,因此遵守密封类遵循的所有规则,所以任何类都不能从enum
(即密封类型)派生。 enum
类型隐式派生自System.Enum
,因此我们不能显式地从System.Enum
派生它。enum
也派生自三个接口:IComparable
、IFormattable
和IConvertible
。- 可以使用许多预定义的转换方法将
enum
从一种数据类型转换为另一种数据类型。 - 可以为多个
enum
成员初始化相同的值。 enum
充当常量,因此其值在初始化后不能改变。- 枚举数名称 '
value__
' 是保留的,不能使用。
结论
通过本文,我们几乎完成了与 enum
相关的所有场景。我们进行了大量的动手实践来澄清我们的概念。我希望我的读者现在能牢记这些基本概念,并且永远不会忘记它们。
这些也可能有助于您通过 C# 面试。
继续编码,享受阅读。
另外,如果您认为我的文章对您有任何帮助,请不要忘记对文章进行评分/评论/点赞,这有助于我获得动力并鼓励我写更多内容。
我的其他系列文章
MVC: https://codeproject.org.cn/Articles/620195/Learning-MVC-Part-Introduction-to-MVC-Architectu
RESTful WebAPIs: https://codeproject.org.cn/Articles/990492/RESTful-Day-sharp-Enterprise-Level-Application
祝您编码愉快!