Bootstrap

【Unity】Unity C#基础(七)枚举、索引器、结构体


枚举

枚举是一组命名整型常量。枚举类型是使用 enum 关键字声明的。
C# 枚举是值类型,存放在栈中。枚举包含自己的值,且不能继承或传递继承

枚举的优点

  1. 枚举能够使代码更加清晰,它允许使用描述性的名称表示整数值
  2. 枚举使代码更易于维护,有助于确保给变量指定合法的、期望的值
  3. 枚举使代码更易输入。

枚举的种类

  1. 简单枚举
  2. 标志枚举(多选枚举)

- 简单枚举

  1. 枚举使用enum关键字来声明,与类同级。枚举本身可以有修饰符,但枚举的成员始终是公共的,不能有访问修饰符。枚举本身的修饰符仅能使用public和internal。
  2. 枚举是值类型,隐式继承自System.Enum,不能手动修改。System.Enum本身是引用类型,继承自System.ValueType。
  3. 枚举都是隐式密封的,不允许作为基类派生子类。
  4. 枚举类型的枚举成员均为静态,且默认为Int32类型。
  5. 每个枚举成员均具有相关联的常数值。此值的类型就是枚举的底层数据类型。每个枚举成员的常数值必须在该枚举的底层数据类型的范围之内。如果没有明确指定底层数据类型则默认的数据类型是int类型。
  6. 枚举成员不能相同,但枚举的值可以相同。

枚举的定义和使用:

enum Week
{ 
	Monday = 1,
	Tuesday = 2,
	Wednesday = 3,
	Sunday = 0,
	Everyday = 1  // 成员的值可以设置成一样的,但是成员不行
}
Console.WriteLine((int)Week.Monday);  // 获取值

显式指定枚举类型:枚举值默认从0开始,逐个递增。

enum sex : byte  // 显式指定枚举的底层数据类型
{ 
	male,   // 0
	female  // 1
}

枚举的各种使用方法:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(Enum.GetName(typeof(Man),1));  // 刘备 (由值获取成员名)
		
        string[] array1 = Enum.GetNames(typeof(Man));
        Console.WriteLine(array1[1]);   // 关羽(按数组下标取成员名)

        Array array2 = Enum.GetValues(typeof(Man));
        Console.WriteLine(array2.GetValue(1));  // 关羽(按数组下标获取枚举值)

        Type t = Enum.GetUnderlyingType(typeof(Man));
        Console.WriteLine(t);       // 输出 Int32(获取枚举类型)

		Console.WriteLine(Man.刘备.ToString());// 获取成员名

        // 由值获取内容
        int i = 1;
        string Name = Enum.Parse(typeof(Man), i.ToString()).ToString();     //此时 Name="刘备"
        Console.WriteLine(Name);

        // 由值获取内容
        string Name2 = "关羽";
        int j = Convert.ToInt32(Enum.Parse(typeof(Man), Name2));     //此时 j=2
        Console.WriteLine(j);

        Console.ReadKey();
    }
}

enum Man
{ 
    刘备 = 1,
    关羽 = 2,
    张飞 = 3
}

- 标志枚举(多选枚举)

标志枚举(多选枚举)使枚举可以实现多选,写法是要在顶部加[System.Flags]特性进行声明。

class Program
{
    static void Main(string[] args)
    {
        var man = Week.White | Week.Rich;     //赋值为011    计算方法001或上010,结果是011
        Console.WriteLine((int)man);
        
        if ((man & Week.White) == Week.White)  // 011 按位与 001,每个位对比,当两边都为1时结果为1,所以结果为 001 
        {
            Console.WriteLine("此人白");
        }
        else
        {
            Console.WriteLine("此人黑");
        }
        Console.ReadKey();
    }
}

[System.Flags]
public enum Week
{ 
    White = 1,  //001
    Rich = 2,  //010
    Beauty = 4  //100
}

- Enum类

C#提供类一个类来方便操作枚举,下面给出这个类的常用方法:
在这里插入图片描述

C#索引器

C#允许一个对象可以像数组一样使用下标的方式访问,下标可以是int,也可以是string,使用方法为在类中写一个this属性。索引器又称为带参属性。当被封装的字段是一个数组的时候,可以考虑使用索引器,操作方便且相对安全

  • 使用索引器可以用类似于数组的方式为对象建立索引。
  • get 访问器返回值。set 访问器分配值。
  • this 关键字用于定义索引器。
  • value 关键字用于定义由 set 索引器分配的值。
  • 索引器不必根据整数值进行索引,由您决定如何定义特定的查找机制。
  • 索引器可被重载。
  • 索引器可以有多个形参,例如当访问二维数组时。
public string this[int index]
{
	get
	{
		string tmp = myList[index];
		return tmp;
	}
	set
	{
		myList[index] = value;
	}
}

// 正常的new,使用下标获取内容
ClassX x = new ClassX();
a[1] = "asdfasdf";

- 使用泛型的索引器

class SampleCollection<T>
{
    private T[] arr = new T[100];
    public T this[int i]
    {
        get
        {
            return arr[i];
        }
        set
        {
            arr[i] = value;
        }
    }
}

// 如何使用这个索引器
class Program
{
    static void Main(string[] args)
    {
        SampleCollection<string> stringCollection = new SampleCollection<string>();
        stringCollection[0] = "Hello, World";
        System.Console.WriteLine(stringCollection[0]);// Hello, World
    }
}

索引器与数组的比较

  • 索引器的索引值不受类型限制。用来访问数组的索引值一定是整数,而索引器可以是其他类型的索引值。
  • 索引器允许重载,一个类可以有多个索引器。
  • 索引器不是一个变量没有直接对应的数据存储地方。索引器有get和set访问器。
  • 索引器允许类和结构的实例按照与数组相同的方式进行索引,索引器类似与属性,不同之处在于他们的访问器采用参数。被称为有参属性。

索引器与属性的比较

  • 标示方式:属性以名称来标识,索引器以函数签名来标识。
  • 索引器可以被重载。属性则不可以被重载。
  • 属性可以为静态的,索引器属于实例成员,不能被声明为static。

结构体

在 C# 中,结构体是值类型数据结构。它使得一个单一变量可以存储各种数据类型的相关数据。struct 关键字用于创建结构体。

- 结构体定义与使用

为了定义一个结构体,您必须使用 struct 语句。struct 语句为程序定义了一个带有多个成员的新的数据类型。
例如,您可以按照如下的方式声明 Book 结构:

struct Books
{
   public string title;
   public string author;
   public string subject;
   public int book_id;
}; 

下面的程序演示了结构的用法:

using System;
using System.Text;
     
struct Books
{
   public string title;
   public string author;
   public string subject;
   public int book_id;
};  

public class testStructure
{
   public static void Main(string[] args)
   {

      Books Book1;        /* 声明 Book1,类型为 Books */
      Books Book2;        /* 声明 Book2,类型为 Books */

      /* book 1 详述 */
      Book1.title = "C Programming";
      Book1.author = "Nuha Ali";
      Book1.subject = "C Programming Tutorial";
      Book1.book_id = 6495407;

      /* book 2 详述 */
      Book2.title = "Telecom Billing";
      Book2.author = "Zara Ali";
      Book2.subject =  "Telecom Billing Tutorial";
      Book2.book_id = 6495700;

      /* 打印 Book1 信息 */
      Console.WriteLine( "Book 1 title : {0}", Book1.title);
      Console.WriteLine("Book 1 author : {0}", Book1.author);
      Console.WriteLine("Book 1 subject : {0}", Book1.subject);
      Console.WriteLine("Book 1 book_id :{0}", Book1.book_id);

      /* 打印 Book2 信息 */
      Console.WriteLine("Book 2 title : {0}", Book2.title);
      Console.WriteLine("Book 2 author : {0}", Book2.author);
      Console.WriteLine("Book 2 subject : {0}", Book2.subject);
      Console.WriteLine("Book 2 book_id : {0}", Book2.book_id);      

      Console.ReadKey();

   }
}

- 结构体的特点

C# 中的结构有以下特点:

  • 结构可带有方法、字段、索引、属性、运算符方法和事件。
  • 结构可定义构造函数,但不能定义析构函数。无参构造函数(默认)是自动定义的,且不能被改变。
  • 与类不同,结构不能继承其他的结构或类。
  • 结构不能作为其他结构或类的基础结构。
  • 结构可实现一个或多个接口。
  • 结构成员不能指定为 abstract、virtual 或 protected。
  • 当您使用 New 操作符创建一个结构对象时,会调用适当的构造函数来创建结构。与类不同,结构体可以不使用 New 操作符即可被实例化。
  • 如果不使用 New 操作符,只有在所有的字段都被初始化之后,字段才被赋值,对象才被使用。
  • 结构体的字段无法赋初始值。

- 类和结构体的区别

类和结构有以下几个基本的不同点:

  • class 是引用类型,struct 是值类型。
  • struct 不支持继承。
  • struct 是值类型,class 是对象类型
  • struct 不能被继承,class 可以被继承
  • struct 默认的访问权限是public,而class 默认的访问权限是private.
  • struct 总是有默认的构造函数,即使是重载默认构造函数仍然会保留。这是因为struct的构造函数是由编译器自动生成的,但是如果重载构造函数,必需对struct中的变量全部初始化。并且struct的用途是那些描述轻量级的对象,例如Line,Point等,并且效率比较高。class在没有重载构造函数时有默认的无参数构造函数,但是一被重载,默认构造函数将被覆盖。
  • struct的new和class的new是不同的。struct的new就是执行一下构造函数创建一个新实例再对所有的字段进行Copy。而class则是在堆上分配一块内存然后再执行构造函数,struct的内存并不是在new的时候分配的,而是在定义的时候分配。

在C#中,我们用struct/class来声明一个类型为值类型/引用类型。考虑下面的例子:

SomeType[] oneTypes = new SomeType[100];
  • 如果SomeType是值类型,则只需要一次分配,大小为SomeType的100倍。
  • 如果SomeType是引用类型,刚开始需要100次分配,分配后数组的各元素值为null,然后再初始化100个元素,结果总共需要进行101次分配。这将消耗更多的时间,造成更多的内存碎片。所以,如果类型的职责主要是存储数据,值类型比较合适。

频繁创建的消耗对比

如果在Update不停实例化一个类,会发生什么?如果在Update不停实例化一个结构体,会发生什么?
class:
就会不断申请堆内存,导致内存碎片不断增多,频繁触发GC,造成掉帧,发热等问题。
struct:
结构体是值类型,程序离开Update这个方法的时候,栈上为变量所分配的内存空间都会被清除。

- 带方法的结构体

using System;
using System.Text;
     
struct Books
{
   private string title;
   private string author;
   private string subject;
   private int book_id;
   public void setValues(string t, string a, string s, int id)
   {
      title = t;
      author = a;
      subject = s;
      book_id =id;
   }
   public void display()
   {
      Console.WriteLine("Title : {0}", title);
      Console.WriteLine("Author : {0}", author);
      Console.WriteLine("Subject : {0}", subject);
      Console.WriteLine("Book_id :{0}", book_id);
   }

};  

public class testStructure
{
   public static void Main(string[] args)
   {

      Books Book1 = new Books(); /* 声明 Book1,类型为 Books */
      Books Book2 = new Books(); /* 声明 Book2,类型为 Books */

      /* book 1 详述 */
      Book1.setValues("C Programming",
      "Nuha Ali", "C Programming Tutorial",6495407);

      /* book 2 详述 */
      Book2.setValues("Telecom Billing",
      "Zara Ali", "Telecom Billing Tutorial", 6495700);

      /* 打印 Book1 信息 */
      Book1.display();

      /* 打印 Book2 信息 */
      Book2.display();

      Console.ReadKey();

   }
}

总结

通常情况下,枚举是最常用的,首先它是值类型,不会导致堆溢出,降低了程序运行风险。且枚举多数用于键值对应,在尽量少占用内存的情况下又能够充分解耦,是最值得使用的类型。

其次是同为值类型的结构体,也是可以使用的,但使用场景其实并不算多。

最后是索引器,一般只在类的属性需要声明为数组的时候使用,是否适用于Unity还有待商榷,毕竟涉及到动态类型,IOS端支持情况尚不确定。

以上是个人对于这三种语法的理解,如有不对的地方,欢迎大家给予指正。

本文部分内容引自:
https://www.cnblogs.com/kissdodog/archive/2013/01/16/2863515.html
https://www.cnblogs.com/promise-7/archive/2012/01/12/2320401.html
https://www.cnblogs.com/seanbrucexxl/p/3878760.html
https://www.runoob.com/csharp/csharp-struct.html
https://zhuanlan.zhihu.com/p/358825256
感谢分享~


更多内容请查看总目录【Unity】Unity学习笔记目录整理

;