C# 7 对应 Unity 版本:
7.1、7.2、7.3 相关内容都是基于 C# 7 的一些改进
C# 7 新增功能和语法:
在声明数值变量时,为了方便查看数值,可以在数值之间插入 “_” 作为分隔符。
主要作用:方便数值变量的阅读。
int i = 9_9123_1239;
print(i); // "991231239"
int i2 = 0xAB_CD_17;
print(i2); // "11259159"
不需要再使用带有 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 修饰临时变量和函数返回值,可以让赋值变为引用传递,即 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
异常类 | 说明 |
---|---|
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 中,模式匹配增强了两个现有的语言结构:
主要作用:节约代码量,提高编程效率
(一)常量模式
(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"
}