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

C# 中的枚举和结构体

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.84/5 (60投票s)

2001 年 10 月 14 日

CPL

4分钟阅读

viewsIcon

481213

downloadIcon

4061

C# 提供的两种常被忽视的值类型及其使用场景

引言

当您使用 C# 时,几乎所有东西都是堆对象。只有基本的原生类型(如 int)被视为值类型。但在 C# 中,有两种值类型比初看之下更有用,它们是 enum(枚举)和 struct(结构体)类型。很少有教程会涵盖这些主题,但它们有各自的用途。而且它们都比类更高效,当它们满足您的要求时,您可以用它们来代替类以提高性能。

Enums

枚举基本上是一组命名的常量。它们在 C# 中使用 enum 关键字声明。每个 enum 类型自动派生自 System.Enum,因此我们可以对我们的枚举使用 System.Enum 方法。枚举是值类型,在栈上创建,而不是在堆上。您无需使用 new 来创建 enum 类型。声明一个 enum 有点像设置数组的成员,如下所示。

enum Rating {Poor, Average, Okay, Good, Excellent}

您可以像传递普通对象一样将枚举传递给成员函数。您也可以对枚举执行算术运算。例如,我们可以编写两个函数,一个用于递增我们的 enum,另一个用于递减我们的 enum

Rating IncrementRating(Rating r)
{
    if(r == Rating.Excellent)
        return r;
    else
        return r+1;
}
Rating DecrementRating(Rating r)
{
    if(r == Rating.Poor)
        return r;
    else
        return r-1;
}

这两个函数都以 Rating 对象作为参数,并返回一个 Rating 对象。现在我们可以从其他地方调用这些函数。

for (Rating r1 = Rating.Poor; 
    r1 < Rating.Excellent ; 
    r1 = IncrementRating(r1))
{           
    Console.WriteLine(r1);
}

Console.WriteLine();

for (Rating r2 = Rating.Excellent; 
    r2 > Rating.Poor; 
    r2 = DecrementRating(r2))
{
    Console.WriteLine(r2);          
}

这是一个示例代码片段,展示了如何对我们的 Enum 对象调用 System.Enum 方法。我们调用 GetNames 方法,它检索枚举中常量的名称数组。

foreach(string s in Rating.GetNames(typeof(Rating)))
    Console.WriteLine(s);

枚举的用途

很多时候,我们会遇到类方法接受自定义选项作为参数的情况。比如,我们有一个文件访问类,其中有一个文件打开方法,该方法有一个参数,可能是只读模式、写入模式、读写模式、创建模式和追加模式中的一种。您可能会考虑在类中为这些模式添加五个静态成员字段。这是错误的方法!声明并使用枚举,它更高效,而且我认为是更好的编程实践。

结构体

在 C++ 中,struct(结构体)在所有方面都与 class(类)几乎相同,唯一的区别在于成员的默认访问修饰符。在 C# 中, structclass 的一个“瘦弱的、可怜的”版本。我不确定这是为什么,但也许他们决定在结构体和类之间划清界限。以下是类和结构体在功能上的一些显著差异。

  • 结构体是栈对象,无论您如何尝试,都无法在堆上创建它们。
  • 结构体不能继承自其他结构体,但可以派生自接口。
  • 您不能为 struct 声明默认构造函数,您的构造函数必须带有参数。
  • 只有当您使用 new 创建 struct 时,构造函数才会被调用;如果您只是声明 struct,就像声明 int 这样的原生类型一样,您必须在能够使用该 struct 之前显式设置每个成员的值。
struct Student : IGrade
{   
    public int maths;
    public int english;
    public int csharp;

    //public member function
    public int GetTot()
    {
        return maths+english+csharp;
    }

    //We have a constructor that takes an int as argument
    public Student(int y)
    {
        maths = english = csharp = y;
    }

    //This method is implemented because we derive
    //from the IGrade interface
    public string GetGrade()
    {
        if(GetTot() > 240 )
            return "Brilliant";
        if(GetTot() > 140 )
            return "Passed";
        return "Failed";
    }
}

interface IGrade
{
    string GetGrade();
}

好了,现在让我们看看如何使用我们的 struct

Student s1 = new Student();
Console.WriteLine(s1.GetTot());
Console.WriteLine(s1.GetGrade());

//Output
0
Failed

这里调用了默认构造函数。这是自动实现的,我们无法拥有自己的无参默认构造函数。默认无参构造函数会将所有值初始化为其零等价物。这就是为什么我们得到 0 作为总计。

Student s2;
s2.maths = s2.english = s2.csharp = 50;
Console.WriteLine(s2.GetTot());
Console.WriteLine(s2.GetGrade());

//Output
150
Passed

因为我们没有使用 new,所以构造函数没有被调用。在所有这些愚蠢的特性中,这一项无疑会赢得年度竞赛。我看不出有什么合理的理由必须这样。无论如何,您必须初始化所有成员字段。如果您注释掉进行初始化的那一行,您将收到一个编译器错误:使用了未分配的局部变量 's2'

Student s3 = new Student(90);
Console.WriteLine(s3.GetTot());
Console.WriteLine(s3.GetGrade());

//Output
270
Brilliant

这次我们使用了带有 int 作为参数的自定义构造函数。

何时使用结构体

因为结构体是值类型,所以它们比类更容易处理,也更高效。当您发现主要使用一个类来存储一组值时,您应该用结构体替换这些类。当您声明结构体数组时,由于它们是在堆上创建的,因此效率会进一步提高。因为如果它们是类,每个类对象都需要在堆上分配内存,并且它们的引用会被存储。实际上,.NET 框架中的许多类实际上是结构体。例如, System.Drawing.Point 实际上是一个 struct 而不是一个 class

© . All rights reserved.