CS4L23——特殊语法
CS4L23——特殊语法
本章代码关键字
1 | var // 隐式类型,用于声明临时变量,编译器会根据=右边的值的类型来判断变量是什么类型的,而不需要再指定变量类型 |
var
隐式类型
var
是一种隐式类型,用于声明临时变量,编译器会根据 =
右边的值的类型来判断变量是什么类型的,而不需要我们自己再指定类型
因此,
var
不能作为类的成员,只能用于临时变量申明时,也就是说一般写在函数语句块中。且var
必须初始化
var
也不能用于里氏替换,也就是父类装子类的情况,父类必须显式指定
1 | static void Main() |
如果需要知道 var
声明的变量实际是什么类型的,直接通过 IDE 来查看即可:
目标类型化 new
注意!此语法是 C# 9 及其以后的版本才可以使用的,这意味着 Unity 2020 及以前的版本不可使用该方法!
具体内容详见:CS5L11——CSharp 9 功能和语法
在已知变量类型的情况下,使用
new
实例化对象时可以直接省略掉new
关键字后面的类型,使变量的赋值语句变得更简单
相比var
,它可以不仅可以用于临时变量初始化时,还可以用于类的成员变量以及对变量的重新赋值的情况实际上,
new()
在所有可以推导出类型的上下文中,都可以使用,包括在初始化器内部,只要能够确定赋值类型是什么不适用此特性的场景包括:
该方法不适用于里氏替换,也就是父类装子类的情况,
new
的子类必须显式指定,否则会出现歧义!使用
as
操作时无法正确推导,以int
为例
1 Console.Write(new() as int); // error: "new()" 没有目标类型使用
var
时也无法推导
1 var x = new(); // error: "new()" 没有目标类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 class Test {}
class Program
{
List<int> numbers = new();
Test obj = new();
static Dictionary<string, List<Test>>? testDict;
static void Main()
{
testDict = new()
{
{"111", new() { new() }}, //这里的第一个new()是List<Test>,后面的new()是Test
{"222", new() { new(), new() }}, //这里的第一个new()是List<Test>,后面的new()是Test
};
}
}
设置对象初始值
声明对象时,可以通过直接写大括号的形式初始化公共成员变量和属性
圆括号在有无参构造的情况下可以省略(使用目标类型化 new() 时不可省略!),
加了圆括号相当于调用了构造函数,再按照大括号快速初始化一遍
1 | class Person |
设置集合初始值
声明集合对象(例如 List<>
等)时,也可以通过大括号直接初始化内部属性
1 | int[] array2 = new int[] { 1, 2, 3, 4, 5 }; |
配合目标类型化 new() 可以进一步省略
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36 class Person
{
private int money;
public bool sex;
public Person() {}
public Person(int money)
{
this.money = money;
}
public string? Name { get; set; }
public int Age { get; set; }
}
class Program
{
static void Main()
{
int[] array2 = new int[] { 1, 2, 3, 4, 5 };
List<int> listInt = new() { 1, 2, 3, 4, 5, 6 };
List<Person> listPerson = new()
{
new(200),
new(100) { Age = 10 },
new() { sex = true, Age = 18, Name = "MrTang" }
};
Dictionary<int, string> dic = new()
{
{1, "123"},
{2, "222"}
};
}
}
匿名类型
var
变量可以申明为自定义的匿名类型,不能直接往里面装方法
1 | var v = new { age = 10, money = 11, name = "小明" }; |
输出:
1 | 10 |
可空类型
一般来说,值类型是不能赋值为空的,但是如果在声明变量时,在值类型后面加 ?
,可以赋值为空
1 | int i1 = null; // error: 无法将 null 转换为“int”,因为后者是不可为 null 的值类型 |
判断可空值类型是否为空
可空值类型本质上是 Nullable<T>
,也就是 C# 为值类型做了一个包装,也就是说可空值类型和原本的值类型并不一样
而 Nullable<T>
有一个 HasValue
属性可以判断是否为 null
1 | int? i = null; |
安全获取可空类型值
如果为值为空,返回默认值类型的默认值,如果此时还传入了参数,会返回传入的参数
1 | int? i = null; |
输出:
1 | 0 |
空引用检查
注意!Unity 的 C# 项目默认关闭了这个,即使设置开启也会被 Unity 关闭,因此这里只针对纯 C# 项目使用,Unity 项目可无视此内容
值得一提的是,C# 8 及其以后的版本为了尽量避免
NullReferenceException
异常(即常见的空引用异常),
引入了空引用检查,并在 .NET 6 及以后的版本默认开启此功能(在csproj
文件内可关闭,需将<Nullable>
改为disable
)启用空引用检查的情况下,引用类型默认将不允许为
null
,包括但不限于以下常见情形会出现警告:
- 对引用类型变量赋值为
null
,编译器会发出警告- 对于返回值为不可为
null
的引用类型的函数,如果返回null
,会被编译器发出警告- 对于类的构造函数,如果存在类型不可为
null
且没有初始化的成员变量或自动属性,构造函数内也没有初始化,那么此构造函数将被警告- 一个不可为
null
的变量接受了一个会返回可空类型的函数的返回值,编译器会发出警告
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 class Program
{
string text;
// warning: 在退出构造函数时,不可为 null 的 字段 "text" 必须包含非 null 值。请考虑添加 "required" 修饰符或将该 字段 声明为可为 null。
public Program() {}
static void Main()
{
string str = null; // warning: 将 null 文本或可能的 null 值转换为不可为 null 类型
string str2 = NullableString(); // warning: 将 null 文本或可能的 null 值转换为不可为 null 类型
}
public string TestNullable()
{
return null; // warning: 可能返回 null 引用
}
public static string? NullableString()
{
return null;
}
}编译器对空引用进行严格检查后,我们即可根据警告去处理问题:
如果一个变量,返回值,参数可能为
null
,那就必须要在类型前加上?
表示为可空类型不可为
null
的成员变量必须要初始化,或者在构造函数内初始化,要么加上?
表示为可空类型根据空引用检查,IDE 可以帮助我们判断一个变量是否可能为
null
,以帮助我们准确的添加null
判断
借助空引用检查,我们可以很好的避免
NullReferenceException
异常
空检查运算符
对于一个可能为空的引用类型变量,直接调用其方法可能会出现 NullReferenceException
异常
为此我们需要在调用时使用 if
语句判断空,而 C# 提供了 ?.
空检查运算符,
相比直接用 .
调用,它会在发现对象为 null
时不调用方法
注意!大部分 Unity 提供的引用类型,诸如
GameObject
、MonoBehaviour
等不可以使用此方法来判断此对象是否存在,
因为 Unity 提供的引用类型重载了!=
,==
运算符判空时的逻辑,使用非 C# 默认的逻辑以判断这些对象是否在 Unity 内被销毁,
而?.
依然使用了 C# 默认的逻辑,就有可能出现虽然不为null
,但实际上在 Unity 内部已经被销毁的情况,导致出错
1 | object? obj = null; |
索引器(?[]
),委托也可以使用
1 | int[] arrayInt = null; |
空合并操作符
空合并操作符 ??
,使用方法:左边值 ??
右边值
如果左边值为 null
就返回右边值 否则返回左边值,只要是可以为 null
的类型都可以用
1 | int? intV = null; |
输出:
1 | 100 |
空复合分配运算符
和 +=
,-=
类似,当出现 variable = variable ?? value
时,可以直接使用复合分配:variable ??= value
,
相当于如果一个变量为 null
,就对其赋值
1 | string? str = null; |
内插字符串
关键符号:$
,用 $
来构造字符串,让字符串中可以拼接变量
1 | string name = "XiaoLi"; |
输出:
1 | 好好学习,XiaoLi,年龄:18 |
单句逻辑省略写法
当 if
或者循环只有一句代码时,就可以省略大括号而直接缩进,有多句代码就不可以省略
1 | string str = ""; |
类里的属性,函数等如果代码块只有一句代码,就可以直接用 =>
而不需要大括号 且不需要 return
其中,属性(索引器同理)只有 get
且 get
语句块只有一句代码时,属性的整个语句块,都可以使用 =>
来简写,=>
后跟要返回对应的表达式语句
1 | class Test |