65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (124投票s)

2014 年 8 月 18 日

CPOL

12分钟阅读

viewsIcon

236198

downloadIcon

5707

第 6 天:理解 C# 中的枚举。我“深入面向对象”系列文章将解释 C# 中的 enum 数据类型。

引言

我“深入面向对象”系列文章将解释 C# 中的 enum 数据类型。我们将通过动手实践来学习,而不仅仅是理论。我们将探索 enum 的强大功能,并涵盖几乎所有可以使用 enum 的场景。我们将遵循一种实用的学习方法来理解这个概念。我们可能会遇到复杂的示例来更深入地理解这个概念。

枚举(定义)

让我们从 MSDN 的定义开始:

enum 关键字用于声明一个枚举,它是一种独立的类型,由一组名为枚举数的命名常量组成。

通常,最好将 enum 直接定义在命名空间内,以便命名空间中的所有类都可以方便地访问它。但是,enum 也可以嵌套在类或结构中。

默认情况下,第一个枚举数为值 0,并且每个连续枚举数的值增加 1。例如,在下面的枚举中,Sat 为 0,Sun 为 1,Mon 为 2,依此类推。”

Enums Akhil

          图片来源:http://pixabay.com/en/job-interview-career-conference-156130/

先决条件

我希望我这篇博文的读者对 C# 有非常基础的了解,我一直希望我的读者在阅读本文时感到愉快。另外,请继续自己编写本文中提供的示例程序,以获得实践经验并更深入地理解概念。

路线图

让我们回顾一下我们的路线图。

                                        

图片来源:http://borgefagerli.com/at-a-crossroads/cross-roads/

  1. 深入OOP(第1天):多态性和继承(早期绑定/编译时多态)
  2. 深入OOP(第2天):多态性和继承(继承)
  3. 深入OOP(第3天):多态性和继承(动态绑定/运行时多态)
  4. 深入OOP(第4天):多态性和继承(C#中抽象类的一切)
  5. 深入OOP(第5天):C#中访问修饰符的一切(Public/Private/Protected/Internal/Sealed/Constants/Readonly字段)
  6. 学习 C#(第 6 天):理解 C# 中的枚举(实用方法)
  7. 学习 C# (第 7 天):C# 中的属性 (实用方法)
  8. 学习 C#(第 8 天):C# 中的索引器(实用方法)
  9. 学习 C#(第 9 天):理解 C# 中的事件(深入探讨)
  10. 学习C#(第10天):C#中的委托(一种实用方法)
  11. 学习C#(第11天):C#中的事件(一种实用方法)

实用方法

Enum 承担着与类几乎相同的责任,即创建一种新的数据类型,并且它与类、接口或结构处于同一级别。

只需打开 Visual Studio 并添加一个名为 Enums 的控制台应用程序。您将得到 Program.cs 类。

注意:本文中的每个代码片段都经过了尝试和测试。

在与 Program 类相同的级别声明一个 enum,称之为 Color

Program.cs

namespace Enums
{
    class Program
    {
        static void Main(string[] args)
        {
        }
    }

    enum Color
    {
        Yellow,
        Blue,
        Brown,
        Green
    }
}

在上述示例中,我们通过 enum 创建了一个新的 datatypedatatypeColor,它有四种不同的值:YellowBlueBrownGreen。我们在声明的 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 的值为 1Brown2Green3

因此,当我们执行 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 的默认 datatypeint,这里我们指定了数据类型为 byte,并得到了结果。

enum 可以指定更多数据类型,如 longulongshortushortintuintbytesbyte

要记住的点:我们不能将 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
    {

    }
}

输出

编译时错误:预期类型为 bytesbyteshortushortintuintlongulong

我们在这里清楚地看到,enum 不能从除了错误中提到的类型之外的任何其他类型派生。

要记住的点enum 不能从除了 bytesbyteshortushortintuintlongulong 类型之外的任何其他类型派生。

让我们从 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)
        {
        }
    }
}

输出

编译时错误:预期类型为 bytesbyteshortushortintuintlongulong

要记住的点enum 类型隐式派生自 System.Enum,因此我们不能显式地从 System.Enum 派生它。

此外,enum 还派生自三个接口:IComparableIFormattableIConvertible

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.YellowColor.Blue 比较时,Yellow 的值小于 Blue,因此返回 -1,第二个语句也一样,当 Color.BlueColor.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 方法,可以直接与声明为 Colorenum 类一起使用。它的第一个参数是 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'

在上面的示例中,我们只是重复了 Colorenum 成员 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 的类,其中包含一个名为 CheckColorstatic 方法,该方法具有一个 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,这导致 Blueyellow 的循环依赖,并导致错误。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 成员,但我不确定。

摘要

让我们回顾一下我们必须记住的所有要点。

  1. enum 代表常量数字,enum 类型被称为具有命名常量的独立类型。
  2. 我们不能将 char 声明为 enum 对象的底层数据类型,因为 char 存储 Unicode 字符,而 enum 对象的 datatype 只能是数字。
  3. enum 不能从除了 bytesbyteshortushortintuintlongulong 类型之外的任何其他类型派生。
  4. 默认情况下,enum 是一个密封类,因此遵守密封类遵循的所有规则,所以任何类都不能从 enum(即密封类型)派生。
  5. enum 类型隐式派生自 System.Enum,因此我们不能显式地从 System.Enum 派生它。
  6. enum 也派生自三个接口:IComparableIFormattableIConvertible
  7. 可以使用许多预定义的转换方法将 enum 从一种数据类型转换为另一种数据类型。
  8. 可以为多个 enum 成员初始化相同的值。
  9. enum 充当常量,因此其值在初始化后不能改变。
  10. 枚举数名称 'value__' 是保留的,不能使用。

结论

通过本文,我们几乎完成了与 enum 相关的所有场景。我们进行了大量的动手实践来澄清我们的概念。我希望我的读者现在能牢记这些基本概念,并且永远不会忘记它们。

这些也可能有助于您通过 C# 面试。

继续编码,享受阅读。

另外,如果您认为我的文章对您有任何帮助,请不要忘记对文章进行评分/评论/点赞,这有助于我获得动力并鼓励我写更多内容。

我的其他系列文章

MVChttps://codeproject.org.cn/Articles/620195/Learning-MVC-Part-Introduction-to-MVC-Architectu

RESTful WebAPIshttps://codeproject.org.cn/Articles/990492/RESTful-Day-sharp-Enterprise-Level-Application

祝您编码愉快!

© . All rights reserved.