C#可空类型教程展示了如何在C#中使用可空值。
null是一种独特的数据类型,表示缺少或不存在的值。NullReferenceException
在尝试取消引用null
对象引用时抛出。为了防止这些异常,我们付出了很多努力。
C#中的两种基本数据类型是值类型和引用类型。不能为值类型分配null
文字。引用类型的默认值为null
引用。
多年来,C#包含许多处理空值的功能。Null安全性是一组特定于可空类型的功能,有助于减少可能的NullReferenceException
发生次数。
C#2.0引入了可空值类型。从那时起,可以使用T?
语法将空值分配给值类型。
C#8.0引入了可为空的引用类型。使用可为空的引用类型,我们可以表达我们的意图,即引用类型可能为空或始终为非空。相同的T?
语法表示引用类型可以为null。
可为空的上下文
可空上下文可以对编译器解释引用类型变量的方式进行细粒度控制。由于.NET6空状态分析和变量注释在新项目中默认启用。
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net6.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> </Project>
在项目级别,可空上下文是使用Nullable
标记设置的。
#nullable enable
可以使用编译器指令将可空上下文的范围限定为C#文件。
C#可空值类型
可空值类型T?
表示其基础值类型T的所有值和一个额外的空值。例如,bool?
变量可以指定为true
、false
或null
之一。
T?
是System.Nullable
结构的简写。HasValue
返回一个值,指示当前System.Nullable
是否具有其基础类型的有效值。Value
从可空值类型中解压值。
可空值类型与值类型具有相同的赋值规则。方法中的局部变量必须在使用前赋值。默认情况下,类中的字段将具有空值。
C#可空示例
在下一个示例中,我们使用可为null的值类型。
Nullable<int> x = 0; Console.WriteLine(x); int? y = 0; Console.WriteLine(y); Console.WriteLine("-------------------------"); x = null; if (x == null) { Console.WriteLine("null value"); } y = null; if (y == null) { Console.WriteLine("null value"); }
在示例中,我们创建了两个可为空的整数变量。首先将它们初始化为零,然后将它们分配为null
。
$ dotnet run 0 0 ------------------------- null value null value
C#检查空值
我们可以使用!=
和isnot
运算符来检查空值。
string?[] words = { "sky", "black", "rock", null, "rain", "cup" }; foreach (var word in words) { if (word != null) { Console.WriteLine($"{word} has {word.Length} letters"); } } Console.WriteLine("-------------------------"); foreach (var word in words) { if (word is not null) { Console.WriteLine($"{word.ToUpper()}"); } }
我们有一个单词列表。为了防止NullReferenceExceptions
,我们确保在调用元素的属性或方法之前每个值都不为null。
使用Nullable
的HasValue
属性,我们检查当前可为null的元素是否具有其基础类型的有效值。
int?[] vals = { 1, 2, 3, null, 4, 5 }; int sum = 0; foreach (var e in vals) { if (e.HasValue) { sum += e.Value; } } Console.WriteLine($"The sum is: {sum}");
在示例中,我们计算整数值的总和。
if (e.HasValue) { sum += e.Value; }
我们使用HasValue
检查元素是否具有某些值,并通过Value
解压该值。
C#空条件运算符
空条件运算符仅将成员访问?.
或元素访问?[]
操作应用于其操作数如果该操作数的计算结果为非空。如果操作数的计算结果为null
,则应用该运算符的结果为null。
var users = new List<User> { new User("John Doe", "gardener"), new User(null, null), new User("Lucia Newton", "teacher") }; users.ForEach(user => Console.WriteLine(user.Name?.ToUpper())); record User(string? Name, string? Occupation);
在示例中,我们有一个包含两个成员的用户记录:姓名
和职业
。我们借助?.
运算符访问对象的名称成员。
users.ForEach(user => Console.WriteLine(user.Name?.ToUpper()));
我们使用?.
访问Name
成员并调用ToUpper
方法。?.
通过不对null
值调用ToUpper
来防止System.NullReferenceException
。
?[]
运算符允许将null
值放入集合中。
int?[] vals = { 1, 2, 3, null, 4, 5 }; int i = 0; while (i < vals.Length) { Console.WriteLine(vals[i]?.GetType()); i++; }
我们在数组中有一个null
值。我们通过对数组元素应用?.
运算符来防止System.NullReferenceException
。
C#参数空异常
当将空引用传递给不接受它作为有效参数的方法时,将抛出ArgumentNullException
。
var words = new List<string?> { "falcon", null, "water", "war", "fly", "wood", "forest", "cloud", "wrath" }; foreach (var word in words) { int n = 0; try { n = CountLtr(word, 'w'); Console.WriteLine($"{word}: {n}"); } catch (ArgumentNullException e) { Console.WriteLine($"{e.Message}"); } } int CountLtr(string? word, char c) { // if (word is null) // { // throw new ArgumentNullException(nameof(word)); // } ArgumentNullException.ThrowIfNull(word); return word.Count(e => e.Equals(c)); }
在示例中,我们抛出并捕获ArgumentNullException
。
$ dotnet run falcon: 0 Value cannot be null. (Parameter 'word') water: 1 war: 1 fly: 0 wood: 1 forest: 0 cloud: 0 wrath: 1
C#null-forgiving运算符
在启用的可空上下文中,允许空值的运算符(!)会抑制编译器警告。运算符在运行时无效。它只影响编译器的静态流分析。
以下示例使用Playwright库;查看Playwright教程以获取更多信息。
using Microsoft.Playwright; using System.Text; using var pw = await Playwright.CreateAsync(); await using var browser = await pw.Chromium.LaunchAsync(); var page = await browser.NewPageAsync(); var ehds = new Dictionary<string, string>{ {"User-Agent", "C# program" } }; await page.SetExtraHTTPHeadersAsync(ehds); var resp = await page.GotoAsync("http://webcode.me/ua.php"); var body = await resp!.BodyAsync(); Console.WriteLine(Encoding.UTF8.GetString(body));
在示例中,我们请求一个资源,该资源返回一个包含所使用的用户的响应。
var resp = await page.GotoAsync("http://webcode.me/ua.php"); var body = await resp!.BodyAsync();
当网站出现连接问题时,响应可能为空。但我们明确表示不担心这个问题。
C#空合并运算符
空合并运算符??
返回其左手操作数的值(如果它不为空);否则,它计算右侧操作数并返回其结果。
var words = new List<string?> { "work", "falcon", null, "cloud", "forest" }; foreach (var word in words) { var e = word ?? "null value"; Console.WriteLine(e); }
我们使用??
运算符来打印“空值”而不是默认的空字符串。
C#空合并赋值运算符
空合并赋值运算符??=
仅当左侧操作数赋值为null
时,才将其右侧操作数的值分配给其左侧操作数。
List<int>? vals = null; vals ??= new List<int> { 1, 2, 3, 4, 5 }; // if (vals == null) // { // vals = new List<int> { 1, 2, 3, 4, 5 }; // } vals.Add(6); vals.Add(7); vals.Add(8); Console.WriteLine(string.Join(", ", vals)); vals ??= new List<int> { 1, 2, 3, 4, 5 }; Console.WriteLine(string.Join(", ", vals));
我们在整数值列表上使用空合并赋值运算符。不使用该运算符的等效代码已被注释掉。
List<int>? vals = null;
首先,列表被分配给null
。
vals ??= new List<int> { 1, 2, 3, 4, 5 };
我们使用??=
为变量分配一个新的列表对象。由于它是null
,因此列表已分配。
vals.Add(6); vals.Add(7); vals.Add(8); Console.WriteLine(string.Join(", ", vals));
我们向列表中添加三个值并打印其内容。
vals ??= new List<int> { 1, 2, 3, 4, 5 };
我们尝试为变量分配一个新的列表对象。由于变量不再是null
,因此不会分配列表。
$ dotnet run 1, 2, 3, 4, 5, 6, 7, 8 1, 2, 3, 4, 5, 6, 7, 8
C#string.IsNullOrWhiteSpace
IsNullOrWhiteSpace
方法指示指定的字符串是null、空还是仅包含空白字符。
var words = new List<string?> { "\t", "falcon", " ", null, "\n", "cloud", "" }; for (int i = 0; i < words.Count; i++) { var e = words[i]; if (string.IsNullOrWhiteSpace(e)) { Console.WriteLine($"{i}: null or white space"); } else { Console.WriteLine($"{i}: {e}"); } }
在示例中,我们对可空字符串元素列表使用了IsNullOrWhiteSpace
方法。
$ dotnet run 0: null or white space 1: falcon 2: null or white space 3: null or white space 4: null or white space 5: cloud 6: null or white space
C#字符串.IsNullOrEmpty
IsNullOrEmpty
方法指示指定的字符串是否为null或空字符串(“”)。
var words = new List<string?> { "\t", "falcon", " ", null, "\n", "cloud", "" }; for (int i = 0; i < words.Count; i++) { var e = words[i]; if (string.IsNullOrEmpty(e)) { Console.WriteLine($"{i}: null or empty"); } else { Console.WriteLine($"{i}: {e}"); } }
在示例中,我们对可空字符串元素列表使用了IsNullOrEmpty
方法。
$ dotnet run 0: 1: falcon 2: 3: null or empty 4: 5: cloud 6: null or empty
在本文中,我们使用了C#中的空值。
列出所有C#教程。