2023-08-15 Untiy进阶 C#知识补充6——C#7主要功能与语法


​ 注意:在此仅提及 Unity 开发中会用到的一些功能和特性,对于不适合在 Unity 中使用的内容会忽略。

​ C# 7 对应 Unity 版本:

  • Unity 2018.3 支持 C# 7
  • Unity 2019.4支持 C# 7.3

​ 7.1、7.2、7.3 相关内容都是基于 C# 7 的一些改进

​ C# 7 新增功能和语法:

  1. 字面值改进
  2. out 内部声明 / 弃元
  3. ref 返回值
  4. 本地函数
  5. 抛出表达式
  6. 元组
  7. 模式匹配

一、字面值改进

​ 在声明数值变量时,为了方便查看数值,可以在数值之间插入 “_” 作为分隔符。

​ 主要作用:方便数值变量的阅读。

int i = 9_9123_1239;
print(i);  // "991231239"

int i2 = 0xAB_CD_17;
print(i2); // "11259159"

二、out 内部声明 / 弃元

​ 不需要再使用带有 out 参数的函数之前,声明对应变量。
​ 作用:简化代码,提高开发效率。

​ 举例:

public class Lesson8 : MonoBehaviour
{
    
    
    public void Calc(out int a, out int b) {
    
    
        a = 10;
        b = 20;
    }

    public void Calc(out float a, out float b) {
    
    
        a = 10;
        b = 20;
    }
}

(一)以往 out 使用方式

public class Lesson8 : MonoBehaviour
{
    
    
    void Start() {
    
    
        int a;
        int b;
        Calc(out a, out b);
    }
}

(二)现在的用法

public class Lesson8 : MonoBehaviour
{
    
    
    void Start() {
    
    
        Calc(out int a, out int b);
        
        print(x); // "10"
        print(y); // "20"
    }
}

​ 可以配合 var 类型使用,但是在函数重载时需要指明哪种类型:

Calc(out var a, out var b); // 报错,不清楚 a、b 是 int 还是 float
Calc(out int a, out var b); // 通过,b 识别为 int

(三)使用弃元符号 “_”

​ 使用 “_” 弃元符号,省略不想使用的参数:

public class Lesson8 : MonoBehaviour
{
    
    
    void Start() {
    
    
        Calc(out int c, out _); // 参数 b 不使用
        print(c);
    }
}

三、ref 返回值

​ 使用 ref 修饰临时变量和函数返回值,可以让赋值变为引用传递,即 C++ 中的 & 引用类型。
​ 作用:用于修改数据对象中的某些值类型变量。

(一)引用变量

public struct TestRef
{
    
    
    public int atk;
    public int def;

    public TestRef(int atk, int def) {
    
    
        this.atk = atk;
        this.def = def;
    }
}

public class Lesson8 : MonoBehaviour
{
    
    
    void Start() {
    
    
        int     testI  = 100;
        ref int testI2 = ref testI; // testI2 与 testI 指向同一块数据内存
        testI2 = 900;
        print(testI);               // "900"

        TestRef     r  = new TestRef(5, 5);
        ref TestRef r2 = ref r;     // r2 与 r 指向同一个类
        r2.atk = 10;
        print(r.atk);               // "10"
    }
}

(二)函数返回值

public class Lesson8 : MonoBehaviour
{
    
    
    // 寻找数组中是否存在 number 成员,并返回其引用
    // 若找不到,则返回第一个成员的引用
    public ref int FindNumber(int[] numbers, int number) {
    
    
        for (int i = 0; i < numbers.Length; i++) {
    
    
            if (numbers[i] == number)
                return ref numbers[i];
        }
        return ref numbers[0];
    }
    
    void Start() {
    
    
        int[]   numbers = new int[] {
    
     1, 2, 3, 45, 5, 65, 4532, 12 };
        ref int number  = ref FindNumber(numbers, 5); // 获取数组中第 5 个成员的引用
        number = 98765;
        print(numbers[4]); // "98765"
    }
}

四、本地函数

​ 在函数内部可以声明一个临时函数。
​ 注意:

  • 本地函数只能在声明该函数的函数内部使用
  • 本地函数可以使用声明自己的函数中的变量

​ 作用:方便逻辑的封装
​ 建议:把本地函数写在主要逻辑的后面,方便代码的查看

public int TestTst(int i) {
    
    
    bool b = false;
    i += 10;
    
    Calc(); // 执行本地函数
    
    print(b);
    return i;

    // 本地函数
    void Calc() {
    
    
        i += 10;
        b =  true;
    }
}

五、抛出表达式

​ 抛出表达式,就是指抛出一个错误。一般的使用方式为:throw 后面 new 一个异常类

​ 异常基类:Exception

表1 C#自带异常类
异常类 说明
IndexOutOfRangeException 当一个数组的下标超出范围时运行时引发
NullReferenceException 当一个空对象被引用时运行时引发
ArgumentException 方法的参数是非法的
ArgumentNullException 一个空参数传递给方法,该方法不能接受该参数
ArgumentOutOfRangeException 参数值超出范围
SystemException 其他用户可处理的异常的基本类
OutOfMemoryException 内存空间不够
StackOverflowException 堆栈溢出
ArithmeticException 出现算术上溢或者下溢
ArrayTypeMismatchException 试图在数组中存储错误类型的对象
BadImageFormatException 图形的格式错误
DivideByZeroException 除零异常
DllNotFoundException 找不到引用的 DLL
FormatException 参数格式错误
InvalidCastException 使用无效的类
InvalidOperationException 方法的调用时间错误
MethodAccessException 试图访问思友或者受保护的方法
MissingMemberException 访问一个无效版本的 DLL
NotFiniteNumberException 对象不是一个有效的成员
NotSupportedException 调用的方法在类中没有实现
InvalidOperationException 当对方法的调用对对象的当前状态无效时,由某些方法引发

​ 在 C# 7 中,可以在更多的表达式中进行错误抛出。
​ 好处:更节约代码量。

(一)空合并操作符后用 throw

private void InitInfo(string str) => jsonStr = str ?? throw new ArgumentNullException(nameof(str));

(二)三目运算符后面用 throw

private string GetInfo(string str, int index) {
    
    
    string[] strs = str.Split(',');
    return strs.Length > index ? strs[index] : throw new IndexOutOfRangeException();
}

(三)=> 符号后面直接 throw

Action action = () => throw new Exception("错了,不准用这个委托");

六、元组

​ 多个值的集合,相当于是一种快速构建数据结构类的方式。

​ 在函数存在多返回值时可以使用元组 (返回值 1 类型, 返回值 2 类型, ....) 来声明返回值;

​ 在函数内部返回具体内容时通过 (返回值 1, 返回值 2, ....) 进行返回。
​ 主要作用:提升开发效率,更方便的处理多返回值等需要用到多个值时的需求。

(一)无变量名元组

(int, float, bool, string) yz = (1, 5.5f, true, "123");

print(yz.Item1); // "1"
print(yz.Item2); // "5.5" 
print(yz.Item3); // "true"
print(yz.Item4); // "123"

(二)有变量名元组

(int i, float f, bool b, string str) yz2 = (1, 5.5f, true, "123");

print(yz2.i);   // "1"
print(yz2.f);   // "5.5" 
print(yz2.b);   // "true"
print(yz2.str); // "123"

(三)元组的比较

​ 元组可以进行等于和不等于的判断:

  • 数量相同才比较;
  • 类型相同才比较;
  • 每一个参数的比较通过 == 比较,都是 true 才认为两个元组相等。
print(yz == yz2); // "true"

(四)成员变量

​ 元组不仅可以作为临时变量,成员变量也是可以的:

public class Lesson9 : MonoBehaviour
{
    
    
    public (int, float) yz;
    
    void Start() {
    
    
        print(this.yz.Item1); // "0",未初始化,int 默认值为 0
    }
}

(五)元组解构

​ 把多返回值元组拆分到不同的变量中:

public class Lesson9 : MonoBehaviour
{
    
    
    private (string str, int i, float f) GetInfo() {
    
    
        return ("123", 2, 5.5f);
    }
    
    void Start() {
    
    
        int    myInt;
        string myStr;
        float  myFloat;
        
        (myStr, myInt, myFloat) = GetInfo(); // 解构
        
        print(myStr);   // "123"
        print(myInt);   // "2"
        print(myFloat); // "5.5"
    }
}

​ 可以简化写成:

(string myStr, int myInt, float myFloat) = GetInfo();

​ 亦可以使用弃元:

(string ss, _, _) = GetInfo(); // 不使用参数 i 和 f
print(ss);

​ 字典中键的应用:

Dictionary<(int i, float f), string> dic = new Dictionary<(int i, float f), string>();
dic.Add((1, 2.5f), "123");

if (dic.ContainsKey((1, 2.5f))) {
    
    
    print("存在相同的键");
    print(dic[(1, 2.5f)]);
}

七、模式匹配

​ 模式匹配是一种语法元素,可以测试一个值是否满足某种条件,并可以从值中提取信息。

​ 在 C# 7 中,模式匹配增强了两个现有的语言结构:

  1. is 表达式:可以在右侧写一个模式语法,而不仅仅是一个类型
  2. switch 语句中的 case

​ 主要作用:节约代码量,提高编程效率

(一)常量模式

(is 常量)

​ 用于判断输入值是否等于某个值。

object o = 1.5f;

if (o is 1) {
    
    
    print("o是1");
}

if (o is null) {
    
    
    print("o是null");
}

(二)类型模式

(is 类型 变量名、case 类型 变量名)

​ 用于判断输入值类型,如果类型相同,将输入值提取出来。

​ 判断某一个变量是否是某一个类型,如果满足会将该变量存入你申明的变量中。

​ 以前的写法:

if (o is int) {
    
    
    int i = (int) o;
    print(i);
}

​ 现在的写法:

if (o is int i) {
    
    
    print(i); // 不打印
}

​ switch 使用:

switch (o) {
    
    
    case int value:
        print("int:" + value);
        break;
    case float value:
        print("float:" + value); // "float:1.5"
        break;
    case null:
        print("null");
        break;
    default:
        break;
}

(三)var 模式

​ 用于将输入值放入与输入值相同类型的新变量中,相当于是将变量装入一个和自己类型一样的变量中。

if (o is var v) {
    
    
    print("v:" + v); // "v:1.5"
}

猜你喜欢

转载自blog.csdn.net/zheliku/article/details/132309735
今日推荐