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#教程。
