Loading

第一章-您第一个Blazor项目

您第一个Blazor项目

使用 Dotnet CLI 生成项目

要使用可在任何机器上运行的 dotnet CLI 生成项目,请首先打开命令行,然后将当前目录更改为您要创建项目的任何位置。 现在执行以下命令来创建一个新的 Blazor WebAssembly 项目。 dotnet 是命令行,采用新指令,模板为 blazorwasm。 --hosted 选项也将生成服务器项目。 最后,我们告诉它生成 MyFirstBlazor 目录中的所有内容。

dotnet new blazorwasm --hosted -o MyFirstBlazor

这个命令需要一点时间,因为它会从 Internet 下载一堆 NuGet 包。 命令准备就绪后,您可以使用

cd MyFirstBlazor
dotnet build

这应该没有任何错误。
现在我们可以使用命令行运行项目

cd MyFirstBlazor/Server
dotnet run

这将显示一些输出,包括 Blazor 应用程序的 URL:

Building...
info: Microsoft.Hosting.Lifetime[14]
Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
Content root path: C:\Code\GitHub\Microsoft.Blazor.3rd\Ch01\MyFirstBlazor

在这个地址上打开你的浏览器(这里是 https://localhost:5001),你就可以开始玩了!

使用 Visual Studio 生成项目

启动 Visual Studio 并选择创建一个新项目。

在搜索框中输入 Blazor,然后选择 Blazor WebAssembly App 项目模板,如图 所示。

image

点击下一步。
将您的项目命名为 MyFirstBlazor,选择应生成项目的位置,然后单击 Next。
在下一个屏幕上,您可以选择要使用的框架。 选择最新版本(在撰写本文时,即 .NET 6.0),将身份验证类型设置为无,选中 ASP.NET Core 托管复选框,然后单击创建。 示例如图所示。

image

等待 Visual Studio 完成。 然后按 F5 构建并运行您的解决方案。片刻之后,浏览器将打开并显示 Blazor 应用程序。

使用 Visual Studio Code 运行 Blazor

像我们使用 CLI 一样创建项目后,使用 VSC 打开解决方案的文件夹(MyFirstBlazor.sln 文件所在的位置)。 您可以从命令提示符代码中执行此操作。

或者您可以打开 VSC,然后选择文件➤ 打开文件夹...。

当 Code 加载完所有内容后(请耐心等待),它会弹出一个问题,如下图所示。 回答是的。 这将添加一个名为 .vscode 的文件夹,其中包含配置文件,从而支持从 Code 构建和运行项目。 如果您已经有一个 vscode 文件夹(例如,因为您复制了一个现有项目),您将不会收到此问题。

image

由于此与 Visual Studio Code 的集成,您只需按 F5 即可构建和运行您的项目。

运行生成的项目

按 F5 或 Ctrl-F5(无调试器)运行(这应该适用于 VS 和 VSC)。 您的(默认)浏览器应该会打开并显示主页,如图所示。

image

这个生成的单页应用程序 (SPA) 在左侧有一个导航菜单,允许您在不同页面之间跳转。 在右侧,您将看到选定的组件; 在上图中,它显示了 Index 组件。 在右上角,有一个指向 https://blazor.net/ 的 About 链接,这是Blazor 官方文档网站。

Index 组件显示强制性的“Hello, world!” 演示,它还包含一个调查组件,您可以单击以填写调查(这是一个真实的调查,所以请让 Microsoft 知道您喜欢 Blazor!)。 SurveyPrompt 是自定义 Blazor 组件的第一个示例。

在导航菜单中,单击计数器链接。 这样做会打开一个带有数字和按钮的简单屏幕,如图 1-16 所示。 单击该按钮将增加计数器。 试试看!

image

在导航菜单中,单击获取数据链接。 在这里,您可以观看(随机和假的)天气预报,如下图 所示。 当客户端询问时,此预测会在服务器上生成。 这一点非常重要,因为客户端(在浏览器中运行)不能直接访问数据库中的数据,所以很多时候,您需要一个可以访问数据库和其他数据存储的服务器。 当然,如果这是 Blazor Server 应用程序,您可以直接访问数据库,因为您在服务器上运行。

image

检查项目的部分

现在能够玩这些页面真是太好了,但让我们看看这一切是如何工作的。 我们将从托管 Blazor 网站的服务器项目开始。 然后我们将查看包含服务器和客户端使用的类的共享项目。最后,我们将检查作为实际 Blazor 实现的客户端项目。

Visual Studio、Visual Studio Code 和 Visual Studio for Mac 使用解决方案文件对将形成应用程序的项目进行分组。 因此,一个典型的 Blazor WebAssembly 项目由一个服务器、一个客户端和一个共享项目组成,这些项目组合成一个解决方案。 这简化了所有内容的构建,因为该解决方案允许工具确定编译所有内容的顺序。 嘿,您甚至可以在 Visual Studio、VS for Mac 和 VSC 之间切换,因为它们都使用相同的项目和解决方案文件!

服务器项目

Web 应用程序是浏览器从服务器下载的一堆文件。根据请求向浏览器提供文件是服务器的工作。 有多种现有服务器可供选择,例如 Windows 上的 IIS 或 Linux 上的 Apache。 ASP.NET Core 有一个称为 Kestrel 的内置服务器,您可以使用 --hosted 选项生成它,然后您可以在 Windows、Linux 或 OSX 上运行它。 这是在开发过程中使用的首选选项。

本书的主题是 Blazor,所以我们不打算讨论生成的服务器项目的所有细节(微软在 https://docs.microsoft.com/aspnet/core 上有很好的 .NET Core 文档 ),但我确实想向您展示一件重要的事情。 在服务器项目(MyFirstBlazor.Server) 中,查找 Program.cs。 打开这个文件并向下滚动到清单所示的配置部分(查找注释)。

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
    app.UseWebAssemblyDebugging();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}
app.UseHttpsRedirection();
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
app.MapRazorPages();
app.MapControllers();
app.MapFallbackToFile("index.html");

配置部分负责安装中间件。 中间件对象是小的 .NET组件,每个组件都有明确的职责。 当您输入一个 URL 时,浏览器会向服务器发送一个 HTTP 请求,然后服务器按列出的顺序将其传递给中间件组件。 其中一些会接受请求并返回响应,其中一些会接受响应并对其进行处理。 查看示例中的第一行。

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
    app.UseWebAssemblyDebugging();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

当服务器有未捕获的异常时,您想查看详细的错误页面吗? 安装一些错误处理中间件的 UseDeveloperExceptionPage 方法可以解决这个问题。 当然,在生产中你不需要它(你应该正确处理所有异常<grin>),所以这个中间件只在开发环境中运行时使用。 服务器如何知道您是在开发中还是在发布中运行? 您在此处看到的 if 语句检查一个名为 ASPNETCORE_ENVIRONMENT 的环境变量,如果环境变量设置为 Development,它就知道您在开发模式下运行。

打开服务器项目的 Properties 文件夹中的 launchSettings.json 文件,如清单所示。 查看 MyFirstBlazor.Server 配置文件。 配置文件中的一项设置将此环境变量设置为 Development,这是编写 Blazor 应用程序时使用的正确选择。

{
    "iisSettings": {
        "windowsAuthentication": false,
        "anonymousAuthentication": true,
        "iisExpress": {
            "applicationUrl": "http://localhost:39361",
            "sslPort": 44358
        }
    },
    "profiles": {
        "MyFirstBlazor.Server": {
            "commandName": "Project",
            "dotnetRunMessages": true,
            "launchBrowser": true,
            "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
            "applicationUrl": "https://localhost:5001;http://localhost:5000",
            "environmentVariables": {
                "ASPNETCORE_ENVIRONMENT": "Development"
            }
        },
        "IIS Express": {
            "commandName": "IISExpress",
            "launchBrowser": true,
            "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
            "environmentVariables": {
                "ASPNETCORE_ENVIRONMENT": "Development"
            }
        }
    }
}

打开服务器项目的 Properties 文件夹中的 launchSettings.json 文件,如上清单所示。 查MyFirstBlazor.Server 配置文件。 配置文件中的一项设置将此环境变量设置为 Development,这是编写 Blazor 应用程序时使用的正确选择。

MapFallbackToFile("index.html") 将返回 index.html 文件,该文件负责加载 Blazor 应用程序所需的所有内容。

使用共享项目

FetchData 组件从服务器下载天气信息。 这些类型的请求将由 MVC 中间件(MapControllers)处理。

需要详细描述预测数据的形状(计算机是挑剔的东西),并且在经典项目中,您会两次描述此模型的形状,一次用于客户端,另一次用于服务器,因为它们会使用不同的语言 – C# on 服务器和客户端上的 JavaScript。 不是 Blazor! 在 Blazor 中,客户端和服务器都使用 C#,因此我们可以描述一次模型并在客户端和服务器之间共享它,如清单所示。 如您所见,这是一个简单的 C# 类,您可以在其他类型的项目中轻松找到。

namespace MyFirstBlazor.Shared;
public class WeatherForecast
{
    public DateTime Date { get; set; }
    public int TemperatureC { get; set; }
    public string? Summary { get; set; }
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

了解客户端 Blazor 项目

打开客户端项目的 wwwroot 文件夹并查找 index.html。 该文件的内容应如清单所示。 老实说,这看起来很像一个普通的 HTML 页面。 但仔细观察,您会发现那里有一个带有 id app 的 div 标签。这是您的 Blazor 应用程序所在的位置。

<div id="app">Loading...</div>

在此之后,还有另一个 div; 这用于在 Blazor 应用程序有未捕获的异常时显示错误。

<div id="blazor-error-ui">
    An unhandled error has occurred.
    <a href="" class="reload">Reload</a>
    <a class="dismiss">🗙</a>
</div>

您还将在结尾处找到一个 <script> 元素。

<script src="_framework/blazor.webassembly.js"></script>

此脚本将通过下载 dotnet.wasm 来安装 Blazor。 再进一步,我们将更详细地研究这一点。

index.html 文件

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport"
              content="width=device-width, initial-scale=1.0, maximum-scale=1.0,
                       user-scalable=no" />
        <title>MyFirstBlazor</title>
        <base href="/" />
        <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
        <link href="css/app.css" rel="stylesheet" />
        <link href="MyFirstBlazor.Client.styles.css" rel="stylesheet" />
    </head>
    <body>
        <div id="app">Loading...</div>
        <div id="blazor-error-ui">
            An unhandled error has occurred.
            <a href="" class="reload">Reload</a>
            <a class="dismiss">🗙</a>
        </div>
        <script src="_framework/blazor.webassembly.js"></script>
    </body>
</html>

从 MyFirstBlazor.Client 项目中打开 Program.cs,如清单所示。 在这里,您会看到 App 组件与 index.html 中的 app div 相关联。 #app 字符串是一个 CSS 选择器,它将找到该 div,Blazor 运行时会将其替换为 App 组件的渲染树。

builder.RootComponents.Add<App>("#app");

mian方法

using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using MyFirstBlazor.Client;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new
Uri(builder.HostEnvironment.BaseAddress) });
await builder.Build().RunAsync();

App 组件所做的主要工作是安装 Router 组件,如清单 所示。 您可以在客户端项目的 App.razor 文件中找到此代码。 路由器负责根据浏览器中的 URL 加载 Blazor 组件。 当找不到路由时,会显示 <NotFound> 内容,目前显示一个简单的未找到消息。 例如,如果您浏览到“/” URL,路由器将查找具有匹配 @page 指令的组件。

//app.razor
<Router AppAssembly="@typeof(App).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData"
                   DefaultLayout="@typeof(MainLayout)" />
        <FocusOnNavigate RouteData="@routeData" Selector="h1" />
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(MainLayout)">
            <p role="alert">Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>

在我们当前的 MyFirstBlazor 项目中,这将匹配您可以在 Pages 文件夹的 Index.razor 文件中找到的 Index 组件。 该组件具有匹配的@page "/" 指令,因此路由器将为 / URL 显示它。 该组件显示 Hello World 消息和调查链接,如清单所示。

//Index.razor

@page "/"
<h1>Hello, world!</h1>
Welcome to your new app.
<SurveyPrompt Title="How is Blazor working for you?" />

布局组件

请开看图。 都有相同的菜单。 此菜单在我们所有的 Blazor 组件之间共享,称为布局组件。但是 Blazor 如何知道哪个组件是布局组件? 再看一下app.razor示例。 找到路由后,它会使用名为 MainLayout 的默认布局组件。 在我们的项目中,布局组件可以在 Shared 文件夹中的 MainLayout.razor 中找到,如清单所示。

//MainLayout.razor 
@inherits LayoutComponentBase
<div class="page">
    <div class="sidebar">
        <NavMenu />
    </div>
    <main>
        <div class="top-row px-4">
            <a href="http://blazor.net" target="_blank" class="ml-md-auto">
                About</a>
        </div>
        <article class="content px-4">
            @Body
        </article>
    </main>
</div>

该组件包含一个带有两个嵌套 divdiv HTML 元素。 第一个带有侧边栏类的嵌套 div 包含一个 Blazor 组件:NavMenu。 这是定义导航菜单的地方。 侧边栏将显示一个菜单,允许您在 HomeCounterFetch 数据之间导航。

下一个带有 main 类的嵌套 div 有两个部分。 第一个是您在每个页面上看到的关于链接。 第二部分包含@Body; 这是显示所选页面的位置。 例如,当您单击导航菜单中的 Counter 链接时,@Body 将替换为 Counter 组件。

调试客户端 Blazor

当然,在构建 Blazor 应用程序时,您会不时遇到意外行为。 可以像使用 Visual Studio 或 Code 的任何 .NET 项目一样调试 Blazor Server。 但是使用 Blazor WebAssembly,您的代码将在浏览器中运行。 您将很高兴得知 VS/VSC 调试器可与 Blazor 一起使用,尽管功能有限。 您可以在代码中放置断点,单步执行代码,并观察包含简单类型(如 bool、int 和 string)的变量。 在撰写本文时,调试 Blazor WebAssembly 仅适用于基于 Chromium 的 Chrome 或 Edge浏览器。

使用 Visual Studio 进行调试

要使用 Visual Studio 启用调试,请从您将用作启动项目的项目中打开 launchSettings.json 文件。 对于托管的 Blazor WebAssembly,这通常是服务器项目。 您需要在此处设置 inspectUri 属性,如清单所示(模板通常会为您配置)。 此属性使 IDE 能够检测到这是一个 Blazor WebAssembly 应用程序,并指示脚本调试基础结构通过 Blazor 的调试代理连接到浏览器。

//launchSettings.json
"MyFirstBlazor.Server": {
    "commandName": "Project",
    "dotnetRunMessages": true,
    "launchBrowser": true,
    "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
    "applicationUrl": "https://localhost:5001;http://localhost:5000",
    "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
    }
},

现在通过按 F5 使用调试器在 VS 中运行您的应用程序。 在您的 Blazor 网站开始运行时请耐心等待(在 Edge 或 Chrome 中!)。 现在你可以在你的代码中放置一个断点,例如,在 Counter 组件的 IncrementCount 方法上,如下图第 17 行。只需单击代码左侧的灰色区域(也称为 gutter),然后 会出现红点,表示调试器将在此代码处停止。

image

返回到您的 Blazor 应用程序,然后单击 Counter 的 Click Me 按钮。 调试器应该在 IncrementCount 方法上停止。 您现在可以在 Locals 窗口中检查简单变量的内容,如图所示。

image

使用 Visual Studio Code 进行调试

启动 VSC。 确保您拥有 Microsoft.AspNetCore.Razor.VSCode.BlazorWasmDebuggingExtension 调试扩展安装如图所示。

image

您还必须确保 JavaScript 预览调试器已启用(在您阅读本文时,它可能不再是预览功能)。 您可以在 VSC 设置中找到此设置,如图所示。

image

现在通过按 F5 使用调试器在 VSC 中运行您的应用程序。 在您的 Blazor 网站开始运行时请耐心等待(在 Chrome 中!)。 现在您可以在代码中设置断点,例如在 Counter 组件的 IncrementCount 方法上,如图第 16 行所示。只需单击代码左侧的区域(也称为 gutter)和红色 点将出现,表示调试器将在此代码处停止。

image

返回到您的 Blazor 应用程序,然后单击 Counter 的 Click Me 按钮。 调试器应该在 IncrementCount 方法上停止。 您现在可以在 Locals 窗口中检查简单变量的内容,如图所示。

image

使用热重载进行开发

在 .NET Core 6.0 中,Microsoft 引入了一个非常好的功能,称为热重载。这允许您在应用程序运行时对代码和标记进行更改。一旦您进行更改,您的应用程序将更新(热重新加载),甚至保持应用程序的现有状态。

使用 .NET CLI 热重载

让我们开始使用命令行界面使用热重载。 打开命令提示符并将目录更改为 MyFirstBlazor Server 项目并运行

dotnet watch

这应该会启动服务器项目(托管 Blazor WebAssembly 项目),并且浏览器也应该随您的应用程序一起打开。

watch : Hot reload enabled. For a list of supported edits, see https://aka.ms/dotnet/hot-reload. Press "Ctrl + Shift + R" to restart.

打开 Counter 组件并将计数器递增几次。 现在对 Counter 组件进行更改,例如。

<h1>My First Counter</h1>

一旦您进行更改,浏览器将自行更新,并保持当前计数!
您还可以更改代码,例如。

private void IncrementCount()
{
    currentCount+=3;
}

节省。 单击“Increment”按钮不会将 3 添加到计数器。
如果要再次重新启动,请返回命令行并按 Ctrl-Shift-R。

使用 Visual Studio 热重载

Blazor WASM 引导过程

在如下代码的底部,您会发现 <script> 元素负责在浏览器中引导 Blazor。 让我们详细看看这个过程。

//index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0,
                                       maximum-scale=1.0, user-scalable=no" />
        <title>MyFirstBlazor2</title>
        <base href="/" />
        <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
        <link href="css/app.css" rel="stylesheet" />
        <link href="MyFirstBlazor2.Client.styles.css" rel="stylesheet" />
    </head>
    <body>
        <div id="app">Loading...</div>
        <div id="blazor-error-ui">
            An unhandled error has occurred.
            <a href="" class="reload">Reload</a>
            <a class="dismiss">🗙</a>
        </div>
        <script src="_framework/blazor.webassembly.js"></script>
    </body>
</html>

运行 Blazor 应用程序。 打开浏览器的开发者工具(大多数浏览器会在你按 F12 时打开开发者工具)。 我们将看看会发生什么网络层。

首先,打开浏览器调试器的应用程序选项卡,然后按清除站点数据按钮,如图所示。 这将清除浏览器的缓存,并让您更好地了解当有人第一次访问 Blazor WebAssembly 应用程序时会发生什么。

image

现在打开浏览器调试器的网络选项卡。 刷新浏览器(清空缓存和硬刷新)查看从服务器下载的内容,如下图所示。首先,您将看到 index.html(显示为 localhost)正在下载,然后下载 bootstrap.min.css 和 app.css,然后是 blazor.webassembly.js。 再低一点,你会看到 blazor.boot.js 被下载,然后又会下载 dotnet.wasm。 这是编译为在 WebAssembly 上运行的 .NET Core 运行时!

image

现在 .NET 运行时正在运行,您将看到(向下滚动?)MyFirstBlazor.Client.dll 已下载,然后是其所有依赖项,包括 mscorlib.dll 和 system.dll。 这些文件包含 .NET 库,其中包含用于执行各种操作的字符串等类,它们与您在服务器上使用的库相同。 这非常强大,因为您可以在 Blazor 中重用现有的 .NET 库或其他人之前构建的库!

在 Network 选项卡的底部,您将看到如上图所示的总下载大小。 差不多10MB! 这是因为我们使用的是空缓存; 下一次下载将显示更少,如上图所示,因为现在 Blazor 可以从缓存中检索大部分文件。

空缓存的总下载大小

image

填充缓存的总下载大小

image

Blazor 服务器引导过程

打开命令行并运行以下命令,这将创建一个新的 Blazor Server 项目和解决方案:

dotnet new blazorserver -o BlazorServerBootstrap

现在我们可以构建并运行这个应用程序:

cd .\BlazorServerBootstrap\
dotnet run

如果您收到类似以下行的错误,则表示另一个项目仍在运行。 首先停止该项目并重试。

Failed to bind to address https://127.0.0.1:5001: address already in use

打开浏览器,然后转到 https://localhost:5001。 应显示 Blazor 应用程序。 现在在“网络”选项卡上打开浏览器的调试器,禁用缓存(在“网络”选项卡上,您有一个禁用缓存的复选框),然后刷新页面。 现在比较下载的内容,如图所示。 如您所见,总下载大小要小得多,从而使您的页面加载速度更快。 您还可以看到在服务器和浏览器之间打开了一个 WebSocket,允许 Blazor 运行时交换 UI 更改和事件。

image

现在单击导航菜单中的 Counter 链接,然后在网络调试器选项卡中选择 websocket 链接。 每次单击时,您都会看到如图所示的几个 SignalR 消息。 这些二进制消息都很小,因为只有更改才能以这种方式传输。 例如,当你点击 Counter 组件中的 Increment 按钮时,浏览器只需要更新浏览器中的数字即可。

image

可空引用类型

每个开发人员都会不时遇到 NullReferenceException,这是一个真正的错误,因为您总是可以避免它。 如果编译器可以帮助您并警告您可能的 NullReferenceException 怎么办? 这就是“可空引用类型”部分的全部内容。

在 C# 中使用 Null

让我们从基础开始。 在 .NET 中,有两种不同的类型:引用类型和值类型。 引用类型使用引用指向对象,值类型保存对象的值。 因此,值类型不能为空。 但是在数据库中,您可以有一列包含一个可以为 nil 的数字(一种值类型)。 那么你如何在 C# 中表示它呢? 为此,我们可以通过在类型后添加一个问号来表示一个可为空的值类型,例如,在如下示例中。

int? i = null;

现在在 C# 中,我们可以告诉编译器以相同的方式处理引用类型,这意味着如果我们将空值分配给引用类型,我们将收到警告,除非我们在类型后添加问号。 如下清单显示了这两个示例。

// No warning
string? canBeNull = null;
// Warning:
// Converting null literal or possible null value to non-nullable type
string cannotBeNull = null;

当然,这会破坏所有现有的 C# 应用程序,因此我们需要在项目属性中启用它。 您可以在 Visual Studio 中使用项目属性执行此操作,如图所示。

image

在项目文件中启用可空引用类型

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net6.0</TargetFramework>
        <Nullable>enable</Nullable>
    </PropertyGroup>
</Project>

这会导致编译器为每个属于(或返回)引用类型的字段、属性和方法设置一个可为空的标志。 您可以通过将鼠标悬停在 VS 中检查此标志,如图所示。

image

然后,如果您尝试使用可为空的引用类型(例如,通过获取字符串的长度),编译器会使用该标志发出警告。 下图将显示一个警告,因为 canBeNull 引用可以为空。

image

但是,如果我们将其嵌套在下图中检查 null 的条件中,编译器将不再发出警告。

image

因此,可空引用类型的整个想法是让编译器进行分析并在我们可能使用可能的空值时发出警告,这将导致 NullReferenceException。

使用参考

当您在启用可空引用类型的情况下执行此操作时,您将收到编译器警告。 为什么? 因为您可以使用 null FirstName 和/或 null LastName 创建 Person 的新实例。 再次,编译器将对此发出警告。

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

有几种方法可以使编译器停止发出警告。 我们可以使用如下清单中的构造函数。 现在我们无法创建具有 null 属性的 Person 实例。 如果有人使用 null 参数调用此构造函数,编译器将再次发出警告。

有几种方法可以使编译器停止发出警告。 我们可以使用如下清单中的构造函数。 现在我们无法创建具有 null 属性的 Person 实例。 如果有人使用 null 参数调用此构造函数,编译器将再次发出警告。

public class Person
{
    public Person(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = lastName;
    }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

有时您根本无法使用构造函数来使编译器静音。 例如,您可能希望将此 Person 类与 Entity Framework Core 一起使用。 在这种情况下,您可以让 FirstNameLastName 为空,如清单所示。

public class Person
{
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
}

容错运算符

有时你只知道一个可为空的引用不是空的,并且你想告诉编译器这件事。 为此,我们可以通过在可空引用上附加感叹号来使用容空运算符,如图所示。 这会将可空标志设置为 false,并且编译器很高兴。

image

我们甚至可以使用它来获得一个空值,并将可空标志设置为 false! 什么?!让我们再看一下 Person 类。 当我们想将此类与 Entity Framework Core 之类的库一起使用并且我们相信该库始终为我们提供非空值时,我们可以使编译器静音,如清单所示。 这看起来很奇怪。 在这里,我们分配 null! 值,其可空标志设置为 false,因此编译器不会给我们警告。

public class Person
{
    public string? FirstName { get; set; } = null!;
    public string? LastName { get; set; } = null!;
}

这正是我们将用来创建具有无法使用构造函数初始化的引用属性的 Blazor 组件的技术。

当然,对于字符串属性,我们也可以为它们分配一个空字符串,而不是如示例中的 null。 但对于其他引用类型,这并不总是可行的。

public class Person
{
    public string? FirstName { get; set; } = string.Empty;
    public string? LastName { get; set; } = string.Empty;
}
posted @ 2022-09-04 13:29  F(x)_King  阅读(586)  评论(0编辑  收藏  举报