13.抛出表达式、元组和模式匹配

13.CSharp各版本新功能和语法-CSharp7功能和语法-元组模式匹配抛出表达式


13.1 知识点

抛出表达式

throw 知识回顾

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

throw new System.Exception("出了最普通的系统错误");

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
//声明一个私有字符串变量jsonStr
private string jsonStr;

//声明一个私有方法InitInfo,用于初始化jsonStr
//参数str: 要初始化jsonStr的字符串
private void InitInfo(string str) => jsonStr = str ?? throw new ArgumentNullException(nameof(str));


InitInfo("123");
三目运算符后面用throw
//声明一个私有方法GetInfo,接受一个字符串和一个索引作为参数
//参数str: 要分割的字符串
//参数index: 要获取的子字符串的索引
private string GetInfo(string str, int index)
{
    //将输入的字符串以逗号为分隔符拆分成字符串数组
    string[] strs = str.Split(',');

    //判断数组长度是否大于索引,如果是,则返回索引处的子字符串;否则抛出IndexOutOfRangeException异常
    return strs.Length > index ? strs[index] : throw new IndexOutOfRangeException();
}

GetInfo("1,2,3", 4);
=>符号后面直接throw
Action action = () => throw new Exception("错了,不准用这个委托");

action();

元组

基本概念:多个值的集合,相当于是一种快速构建数据结构类的方式。元组是值类型的,一般在函数存在多返回值时可以使用元组 (返回值1类型,返回值2类型,….) 来声明返回值,在函数内部返回具体内容时通过 (返回值1,返回值2,….) 进行返回。
主要作用:提升开发效率,更方便的处理多返回值等需要用到多个值时的需求

元组的基本使用

// 1. 无变量名元组的声明
//声明一个包含 int、float、bool 和 string 类型的元组 yz1,依次为 1、5.5f、true 和 "123"
(int, float, bool, string) yz1 = (1, 5.5f, true, "123");
print(yz1.Item1);    // 访问元组 yz1 的第一个元素
print(yz1.Item2);    // 访问元组 yz1 的第二个元素
print(yz1.Item3);    // 访问元组 yz1 的第三个元素
print(yz1.Item4);    // 访问元组 yz1 的第四个元素

// 2. 有变量名元组的声明
//声明一个包含 int、float、bool 和 string 类型的元组 yz2,依次为 1、5.5f、true 和 "123"
(int i, float f, bool b, string str) yz2 = (1, 5.5f, true, "123");
print(yz2.i);        // 访问元组 yz2 的 i 变量(代表第一个元素)
print(yz2.f);        // 访问元组 yz2 的 f 变量(代表第二个元素)
print(yz2.b);        // 访问元组 yz2 的 b 变量(代表第三个元素)
print(yz2.str);      // 访问元组 yz2 的 str 变量(代表第四个元素)

// 3. 元组可以进行等于和不等于的判断
// 如果两个元组的元素数量相同且类型相同,则进行元素逐一比较,如果每个元素都相等则认为两个元组相等
if (yz1 == yz2)
    print("相等");
else
    print("不相等");

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

//元组成员变量
public (int, float) yz;

//元组不仅可以作为临时变量 成员变量也是可以的
print(this.yz.Item1);

元组作为函数返回值

// 声明一个私有方法GetInfo,返回一个元组(string, int, float)
private (string str, int i, float f) GetInfo()
{
    // 返回一个元组,包含了字符串"123"、整数2和浮点数5.5f
    return ("123", 2, 5.5f);
}

//无返回值参数变量名得到函数返回值
var info = GetInfo();
print(info.Item1);    // 访问元组 info 的第一个元素
print(info.Item2);    // 访问元组 info 的第二个元素
print(info.Item3);    // 访问元组 info 的第三个元素

//有返回值参数变量名得到函数返回值
print(info.f);        // 访问元组 info 的 f 变量(代表第三个元素)
print(info.i);        // 访问元组 info 的 i 变量(代表第二个元素)
print(info.str);      // 访问元组 info 的 str 变量(代表第一个元素)

//元组的结构赋值
//相当于把多返回值元组拆分到不同的变量中
// 将 GetInfo() 方法返回的元组的值分别赋给 myStr、myInt 和 myFloat 这三个变量
int myInt;
string myStr;
float myFloat;
(myStr, myInt, myFloat) = GetInfo();
//(string myStr, int myInt, float myFloat) = GetInfo();//也可以写在里面 不在外面声明 
//但是要不都在外面声明 要不就都写在里面 不能混用
print(myStr);
print(myInt);
print(myFloat);

//丢弃参数
//利用传入 下划线_ 达到丢弃该参数不使用的作用
//使用下划线 (_) 作为占位符来表示不关心的元组成员,这样可以忽略这些成员的值
(string ss, _, _) = GetInfo();
print(ss);

元组作为字典泛型

// 字典中的键 需要用多个变量来控制

// 创建一个字典,键是元组 (int, float),值是字符串类型
Dictionary<(int i, float f), string> dic = new Dictionary<(int i, float f), string>();

// 向字典中添加一个键值对,键是 (1, 2.5f),值是 "123"
dic.Add((1, 2.5f), "123");

// 检查字典中是否存在指定的键
if (dic.ContainsKey((1, 2.5f)))
{
    print("存在相同的键");        // 输出存在相同的键
    print(dic[(1, 2.5f)]);       // 输出键对应的值
}

模式匹配

基本概念:模式匹配时一种语法元素,可以测试一个值是否满足某种条件,并可以从值中提取信息。在C#7中,模式匹配增强了两个现有的语言结构:1. is表达式,is表达式可以在右侧写一个模式语法,而不仅仅是一个类型2. switch语句中的case
主要作用:节约代码量,提高编程效率

object o = 1.5f;

// 1.常量模式(is 常量):用于判断输入值是否等于某个值
if (o is 1)
{
    print("o是1");        // 输出 o 是1
}
if (o is null)
{
    print("o是null");     // 输出 o 是 null
}

// 2.类型模式(is 类型 变量名、case 类型 变量名):用于判断输入值类型,如果类型相同,将输入值提取出来
// 判断某一个变量是否是某一个类型,如果满足会将该变量存入你声明的变量中

// 以前的写法
// if (o is int)
// {
//    int i = (int)o;
//    print(i);
// }

// 现在的写法 如果 o 是 int 类型的,就把 o 的值放到 i 上存储
if (o is int i)
{
    print(i);        // 输出 i 的值(1)
}

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

// 3.var模式:用于将输入值放入与输入值相同类型的新变量中
//            相当于是将变量装入一个和自己类型一样的变量中
if (o is var v)
{
    print(o);               // 输出 o 的值(1.5)
    print(v);               // 输出 v 的值(1.5)
    print(v.GetType());     // 输出 v 的类型(System.Single)
}

总结

元组和模式匹配知识点是C# 7中引入的最重要的两个知识点,它们可以帮助我们更有效率地完成一些功能需求。建议大家常用它们。


13.2 知识点代码

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Lesson13_CSharp各版本新功能和语法_CSharp7功能和语法_元组模式匹配抛出表达式 : MonoBehaviour
{
    #region 知识点二 元组

    //元组成员变量
    public (int, float) yz;

    #endregion

    void Start()
    {
        #region 知识点一 抛出表达式

        //throw 知识回顾
        //抛出表达式,就是指抛出一个错误
        //一般的使用方式 都是 throw后面 new 一个异常类
        //异常基类:Exception
        //throw new System.Exception("出了最普通的系统错误");

        #region C#自带异常类
        //常见
        //IndexOutOfRangeException:当一个数组的下标超出范围时运行时引发。
        //NullReferenceException:当一个空对象被引用时运行时引发。
        //ArgumentException:方法的参数是非法的
        //ArgumentNullException: 一个空参数传递给方法,该方法不能接受该参数
        //ArgumentOutOfRangeException: 参数值超出范围
        //SystemException:其他用户可处理的异常的基本类
        //OutOfMemoryException:内存空间不够
        //StackOverflowException 堆栈溢出

        //ArithmeticException:出现算术上溢或者下溢
        //ArrayTypeMismatchException:试图在数组中存储错误类型的对象
        //BadImageFormatException:图形的格式错误
        //DivideByZeroException:除零异常
        //DllNotFoundException:找不到引用的DLL
        //FormatException:参数格式错误
        //InvalidCastException:使用无效的类
        //InvalidOperationException:方法的调用时间错误
        //MethodAccessException:试图访问思友或者受保护的方法
        //MissingMemberException:访问一个无效版本的DLL
        //NotFiniteNumberException:对象不是一个有效的成员
        //NotSupportedException:调用的方法在类中没有实现
        //InvalidOperationException:当对方法的调用对对象的当前状态无效时,由某些方法引发。
        #endregion

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

        //1.空合并操作符后用throw
        //InitInfo("123");
        //2.三目运算符后面用throw
        //GetInfo("1,2,3", 4);
        //3.=>符号后面直接throw
        //Action action = () => throw new Exception("错了,不准用这个委托");
        //action();

        #endregion

        #region 知识点二 元组

        //基本概念:多个值的集合,相当于是一种快速构建数据结构类的方式 元组是值类型的
        //         一般在函数存在多返回值时可以使用元组 (返回值1类型,返回值2类型,....) 来声明返回值
        //         在函数内部返回具体内容时通过 (返回值1,返回值2,....)  进行返回
        //主要作用:提升开发效率,更方便的处理多返回值等需要用到多个值时的需求

        #region 元组的基本使用

        // 1. 无变量名元组的声明
        // 声明一个包含 int、float、bool 和 string 类型的元组 yz1,依次为 1、5.5f、true 和 "123"
        (int, float, bool, string) yz1 = (1, 5.5f, true, "123");
        print(yz1.Item1);    // 访问元组 yz1 的第一个元素
        print(yz1.Item2);    // 访问元组 yz1 的第二个元素
        print(yz1.Item3);    // 访问元组 yz1 的第三个元素
        print(yz1.Item4);    // 访问元组 yz1 的第四个元素

        // 2. 有变量名元组的声明
        // 声明一个包含 int、float、bool 和 string 类型的元组 yz2,依次为 1、5.5f、true 和 "123"
        (int i, float f, bool b, string str) yz2 = (1, 5.5f, true, "123");
        print(yz2.i);        // 访问元组 yz2 的 i 变量(代表第一个元素)
        print(yz2.f);        // 访问元组 yz2 的 f 变量(代表第二个元素)
        print(yz2.b);        // 访问元组 yz2 的 b 变量(代表第三个元素)
        print(yz2.str);      // 访问元组 yz2 的 str 变量(代表第四个元素)

        // 3. 元组可以进行等于和不等于的判断
        // 如果两个元组的元素数量相同且类型相同,则进行元素逐一比较,如果每个元素都相等则认为两个元组相等
        if (yz1 == yz2)
            print("相等");
        else
            print("不相等");


        //元组不仅可以作为临时变量 成员变量也是可以的
        print(this.yz.Item1);

        #endregion

        #region 元组的应用——函数返回值

        //无变量名函数返回值
        var info = GetInfo();
        print(info.Item1);    // 访问元组 info 的第一个元素
        print(info.Item2);    // 访问元组 info 的第二个元素
        print(info.Item3);    // 访问元组 info 的第三个元素

        //有变量名
        print(info.f);        // 访问元组 info 的 f 变量(代表第三个元素)
        print(info.i);        // 访问元组 info 的 i 变量(代表第二个元素)
        print(info.str);      // 访问元组 info 的 str 变量(代表第一个元素)

        //元组的结构赋值
        //相当于把多返回值元组拆分到不同的变量中
        // 将 GetInfo() 方法返回的元组的值分别赋给 myStr、myInt 和 myFloat 这三个变量
        int myInt;
        string myStr;
        float myFloat;
        (myStr, myInt, myFloat) = GetInfo();
        //(string myStr, int myInt, float myFloat) = GetInfo();//也可以写在里面 不在外面声明 但是要都在外面声明或者都写在里面 不能混用
        print(myStr);
        print(myInt);
        print(myFloat);

        //丢弃参数
        //利用传入 下划线_ 达到丢弃该参数不使用的作用
        //使用下划线 (_) 作为占位符来表示不关心的元组成员,这样可以忽略这些成员的值
        (string ss, _, _) = GetInfo();
        print(ss);

        #endregion

        #region 元组的应用——字典

        // 字典中的键 需要用多个变量来控制

        // 创建一个字典,键是元组 (int, float),值是字符串类型
        Dictionary<(int i, float f), string> dic = new Dictionary<(int i, float f), string>();

        // 向字典中添加一个键值对,键是 (1, 2.5f),值是 "123"
        dic.Add((1, 2.5f), "123");

        // 检查字典中是否存在指定的键
        if (dic.ContainsKey((1, 2.5f)))
        {
            print("存在相同的键");        // 输出存在相同的键
            print(dic[(1, 2.5f)]);       // 输出键对应的值
        }

        #endregion

        #endregion

        #region 知识点三 模式匹配

        //基本概念:模式匹配时一种语法元素,可以测试一个值是否满足某种条件,并可以从值中提取信息
        //         在C#7中,模式匹配增强了两个现有的语言结构
        //         1.is表达式,is表达式可以在右侧写一个模式语法,而不仅仅是一个类型
        //         2.switch语句中的case
        //主要作用:节约代码量,提高编程效率

        object o = 1.5f;

        // 1.常量模式(is 常量):用于判断输入值是否等于某个值
        if (o is 1)
        {
            print("o是1");        // 输出 o 是1
        }
        if (o is null)
        {
            print("o是null");     // 输出 o 是 null
        }

        // 2.类型模式(is 类型 变量名、case 类型 变量名):用于判断输入值类型,如果类型相同,将输入值提取出来
        // 判断某一个变量是否是某一个类型,如果满足会将该变量存入你声明的变量中

        // 以前的写法
        // if (o is int)
        // {
        //    int i = (int)o;
        //    print(i);
        // }

        // 现在的写法 如果 o 是 int 类型的,就把 o 的值放到 i 上存储
        if (o is int i)
        {
            print(i);        // 输出 i 的值(1)
        }

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

        // 3.var模式:用于将输入值放入与输入值相同类型的新变量中
        //            相当于是将变量装入一个和自己类型一样的变量中
        if (o is var v)
        {
            print(o);               // 输出 o 的值(1.5)
            print(v);               // 输出 v 的值(1.5)
            print(v.GetType());     // 输出 v 的类型(System.Single)
        }

        #endregion

        #region 总结
        //元组和模式匹配知识点 是C# 7中引入的最重要的两个知识点
        //他们可以帮助我们更效率的完成一些功能需求
        //建议大家常用他们
        #endregion
    }

    #region 知识点一 抛出表达式

    // 声明一个私有字符串变量jsonStr
    private string jsonStr;

    // 声明一个私有方法InitInfo,用于初始化jsonStr
    // 参数str: 要初始化jsonStr的字符串
    private void InitInfo(string str) => jsonStr = str ?? throw new ArgumentNullException(nameof(str));

    // 声明一个私有方法GetInfo,接受一个字符串和一个索引作为参数
    // 参数str: 要分割的字符串
    // 参数index: 要获取的子字符串的索引
    private string GetInfo(string str, int index)
    {
        // 将输入的字符串以逗号为分隔符拆分成字符串数组
        string[] strs = str.Split(',');

        // 判断数组长度是否大于索引,如果是,则返回索引处的子字符串;否则抛出IndexOutOfRangeException异常
        return strs.Length > index ? strs[index] : throw new IndexOutOfRangeException();
    }
    #endregion

    #region 知识点二 元组

    // 声明一个私有方法GetInfo,返回一个元组(string, int, float)
    private (string str, int i, float f) GetInfo()
    {
        // 返回一个元组,包含了字符串"123"、整数2和浮点数5.5f
        return ("123", 2, 5.5f);
    }

    #endregion
}

13.3 练习题

写一个Monster类,它派生出Boss和Goblin两个类,Boss有技能;小怪有攻击;随机生成10个怪,装载到数组中,遍历这个数组,调用他们的攻击方法,如果是boss就释放技能(利用模式匹配中的类型模式结合switch语法来制作)

class Monster
{
    public virtual void Attack()
    {
        print("怪物发起攻击!");
    }
}

class Boss : Monster
{
    public void UseSkill()
    {
        print("Boss释放技能!");
    }
}

class Goblin : Monster
{
    public override void Attack()
    {
        print("小怪发起攻击!");
    }
}

Monster[] monsters = new Monster[10];

for (int i = 0; i < monsters.Length; i++)
{
    if (Random.Range(0, 2) == 0)
        monsters[i] = new Boss();
    else
        monsters[i] = new Goblin();
}

foreach (var monster in monsters)
{
    switch (monster)
    {
        case Boss boss:
            boss.UseSkill();
            break;
        case Goblin goblin:
            goblin.Attack();
            break;
        default:
            monster.Attack();
            break;
    }
}

请实现一个函数的多返回值(利用元组)

public (int,float,bool,string) YuanZhuDefaultFun()
{
    return (default, default, default, default);
}

(int, float, bool, string) yzdf = YuanZhuDefaultFun();
print(yzdf.Item1);//0
print(yzdf.Item2);//0
print(yzdf.Item3);//False
print(yzdf.Item4);//Null

请写出以下报错的中文含义,以及可能造成该错误的原因

  • IndexOutOfRangeException:数组的下标超出范围时运行时引发;数组或者列表越界
  • NullReferenceException :当一个空对象被引用时运行时引发;某一个变量没有赋值就使用它(比如忘记拖拽忘记关联)
  • ArgumentNullException:空参数传递给方法,该方法不能接受该参数;传入的参数有问题
  • OutOfMemoryException:内存空间不够;项目的内存优化做的有问题,内存占用太大,可能存在内存移除或者资源过大
  • StackOverflowException:堆栈溢出;死循环或者递归函数无限递归

13.4 练习题代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Lesson13_练习题 : MonoBehaviour
{
    #region 练习题一

    class Monster
    {
        public virtual void Attack()
        {
            print("怪物发起攻击!");
        }
    }

    class Boss : Monster
    {
        public void UseSkill()
        {
            print("Boss释放技能!");
        }
    }

    class Goblin : Monster
    {
        public override void Attack()
        {
            print("小怪发起攻击!");
        }
    }

    #endregion

    void Start()
    {
        #region 练习题一
        //写一个Monster类,它派生出Boss和Goblin两个类,Boss有技能;小怪有攻击;
        //随机生成10个怪,装载到数组中,遍历这个数组,调用他们的攻击方法,如果是boss就释放技能(利用模式匹配中的类型模式结合switch语法来制作)

        Monster[] monsters = new Monster[10];

        for (int i = 0; i < monsters.Length; i++)
        {
            if (Random.Range(0, 2) == 0)
                monsters[i] = new Boss();
            else
                monsters[i] = new Goblin();
        }

        foreach (var monster in monsters)
        {
            switch (monster)
            {
                case Boss boss:
                    boss.UseSkill();
                    break;
                case Goblin goblin:
                    goblin.Attack();
                    break;
                default:
                    monster.Attack();
                    break;
            }
        }

        #endregion

        #region 练习题二
        //请实现一个函数的多返回值(利用元组)

        (int, float, bool, string) yzdf = YuanZhuDefaultFun();
        print(yzdf.Item1);//0
        print(yzdf.Item2);//0
        print(yzdf.Item3);//False
        print(yzdf.Item4);//Null
        #endregion

        #region 练习题三

        //请写出以下报错的中文含义,以及可能造成该错误的原因
        //IndexOutOfRangeException
        //NullReferenceException
        //ArgumentNullException
        //OutOfMemoryException
        //StackOverflowException

        //IndexOutOfRangeException:数组的下标超出范围时运行时引发;数组或者列表越界

        //NullReferenceException :当一个空对象被引用时运行时引发;某一个变量没有赋值就使用它(比如忘记拖拽忘记关联)

        //ArgumentNullException:空参数传递给方法,该方法不能接受该参数;传入的参数有问题

        //OutOfMemoryException:内存空间不够;项目的内存优化做的有问题,内存占用太大,可能存在内存移除或者资源过大

        //StackOverflowException:堆栈溢出;死循环或者递归函数无限递归

        #endregion
    }

    #region 练习题二

    public (int,float,bool,string) YuanZhuDefaultFun()
    {
        return (default, default, default, default);
    }

    #endregion
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏