跨平台 UI 应用
.NET6 对多平台(Android、IOS等)界面应用程序做了统一,提供了在多种平台和设备上一致的体验,并且可以在移动应用和 PC桌面程序之间共享更多的代码。多平台统一工具包是基于 Xamarin.Forms 的集成和拓展,使得我们可以开发 Windows、MacOS、Android 和 IOS 桌面应用。
目前 Visual Studio 支持 Windows 和 MacOS,在已经安装了 .NET6 SDK 的机器上,发布的桌面程序可以在 Windows 和 MacOS 上运行,如果要支持 Android 和 IOS ,则需要下载另外两个包。这是因为 Windows 和 MacOS 可以通过安装 .NET6 Runtime 去运行程序,运行的是 .dll 文件(IL中间代码),而 Android 和 IOS 都是发布和运行本机代码。
# Windows 下载 Microsoft.NET.Workload.Android.11.0.200.85.msi Microsoft.NET.Workload.iOS.14.3.100-ci.main.1079.msi # MacOS 下载 Microsoft.NET.Workload.Android-11.0.200-ci.master.85.pkg Microsoft.iOS.Bundle.14.3.100-ci.main.1079.pkg
读者可以到 https://github.com/dotnet/net6-mobile-samples 查找下载链接,发现不开代理下载不了,各位请注意。
.NET6 运行时有 android 和 ios 的特殊标记,要支持移动应用,需要指定名称,如要支持 Android,在 .csproj 文件中:
<TargetFramework>net6.0-android</TargetFramework>
下面是一个 Xamarin.Forms 应用的 .csproj 模板:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFrameworks>net6.0-android;net6.0-ios</TargetFrameworks> <RuntimeIdentifier>ios-x64</RuntimeIdentifier> <OutputType>Exe</OutputType> </PropertyGroup> <ItemGroup> <PackageReference Include="Xamarin.Forms" Version="4.8.0.1364" GeneratePathProperty="true" /> </ItemGroup> </Project>
当然,要发布或者调试程序,需要 SDK 或 仿真器,例如安卓需要 Android SDK以及Android SDK Platform 30
,发布的时候也需要指定:
dotnet build HelloForms -t:Run -f net6.0-android dotnet build HelloForms -t:Run -f net6.0-ios
官方的示例项目中,nuget 包需要访问 pkgs.dev.azure.com,不代理访问不了。因此,这个示例项目搞不了,大家看着过过瘾就行。
目前 .NET6 Targeting 有:
net6.0
net6.0-android
net6.0-ios
net6.0-maccatalyst
net6.0-macos
net6.0-tvos
net6.0-windows
Blazor 桌面应用
我想,看到这个更新,最开心的是 Ant Design of Blazor 项目的主导作者 James Yeung 吧。
.NET6 中,可以将 Blazor 拓展混合到 UI 应用程序中,将 Web 和本机 UI 结合在一起,可以在桌面中嵌入运行。Blazor 混合开发的方式,称为 Blazor Hybrid Apps。
.NET Core 3.0 支持 Blazor Server,3.1 支持 Blazor WebAssembly,Blazor WebAssembly 使用 Mono 运行时,很多 .NETer 应该体验过 Blazor 开发,WebAssembly 目前来看还是太大了(.NET Core 3.x),还有性能差等问题。后续 .NET 版本对其进行了大量优化。
VS2019 Preview4 中这个模板名称变成了 Blazor WebAssembly App ,实际上就是 WebAssembly,加上了 App 这个名称,但是并不代表是 UI 程序,它依然是 web。
回归正题,.NET6 更新的博客说这个 Blazor 可以跟 MAUI一起搞,但是博客并没有提到 Demo 和实现细节,可能还在继续开发中,Preview 1 可能暂时看不了。那 TM 我下载 VS2019 Preview4 后,还是不能直接创建???耍我呢?怎么体验这个 Blazor 开发桌面应用?
不久前有人创建了这个 Issue:https://github.com/Webreaper/Damselfly/issues/108
不过,Blazor 倒是可以跟移动应用搞一起,有个项目叫 Mobile Blazor Bindings,可以将 Blazor 结合到 Xamarin 中,这个项目在 .NET Core 3.x 就已经支持了。
项目地址:https://github.com/dotnet/MobileBlazorBindings
示例代码:
<StackLayout> <Label FontSize="30">You pressed @count times </Label> <Button Text="+1" OnClick="@HandleClick" /> </StackLayout> @code { int count; void HandleClick() { count++; } }
这种项目,使用的是 Microsoft.MobileBlazorBindings.Templates 库,这个库封装了大量的 Razor 组件。
我们可以通过 dotnet 命令来创建这种项目,对应模板如下:
Template Name Short Name Language Tags ---------------------------------------------- -------------------- ---------- ------------------------ Experimental Mobile Blazor Bindings App mobileblazorbindings [C#] Blazor/Xamarin.Forms Experimental Mobile Blazor Bindings Hybrid App blazorhybrid [C#] Blazor/Xamarin.Forms/Web
mobileblazorbindings 不好玩,大家不要试。
我们可以使用命令来创建模板项目:
dotnet new blazorhybrid
目录结构: H:. ├─HBlazor │ ├─WebUI │ │ ├─Pages │ │ └─Shared │ └─wwwroot ├─HBlazor.Android │ ├─Assets │ ├─Properties │ ├─Resources │ └─wwwroot ├─HBlazor.iOS │ ├─Assets.xcassets │ ├─Properties │ └─Resources │ └─wwwroot ├─HBlazor.macOS │ ├─Assets.xcassets │ └─Resources │ └─wwwroot └─HBlazor.Windows └─wwwroot
但是官方的示例项目有 Bug,而且项目莫名其妙异常,大家试着玩一下就行了。
这个原理是 Microsoft.MobileBlazorBindings 封装了一系列的 razor 组件,然后我编写 razor 文件,引用这些组件编写界面以及动态操作。
测试代码(Main.razor):
<ContentView> <StackLayout> <StackLayout Margin="new Thickness(20)" Orientation="StackOrientation.Horizontal"> <Label Text="痴者工良" /> <Image Source="@(new FileImageSource { File = "H:/文章/.NET6/mobileblazorbindings/HBlazor/Bugs.jpg" })" /> <Label Text="@($"Bug - {CounterState.CurrentCount}")" FontSize="40" HorizontalOptions="LayoutOptions.StartAndExpand" /> <Button Text="解决 Bug" OnClick="@CounterState.IncrementCount" VerticalOptions="LayoutOptions.Center" Padding="10" /> </StackLayout> <BlazorWebView VerticalOptions="LayoutOptions.FillAndExpand"> <HBlazor.WebUI.App /> </BlazorWebView> </StackLayout> </ContentView>
注:目前不支持 HTML 标签 如<br />
,现在可以使用 Razor 语法,但是还不能直接使用 HTML。 猜测可能是将 Razor 转换到 Xaml ,所以只能使用里面已经定义好的组件。
怎么加入 CSS 也是个问题。这也说明了,不支持那些 js !
而 .NET6 博文中提到的 Blazor desktop apps,看界面样子,应该是网页版本嵌入的。但是目前还不能尝试,所以无法测试,跳过。
回顾一下跨平台 UI 应用,主要提到 Xamarin 和 Blazor,而 .NET6 会出现一个新的跨平台 APP UI 框架,叫 MAUI,.NET MAUI是Xamarin.Forms 的演进,这个在 2020 年时就出现了。这个库的目的是统一 Xamarin SDK 到 .NET 中,并且能够与其它项目共享代码(如Blazor)。
目前有两种方式可以尝试 MAUI:
后者已经提到了,MVU 这种读者有兴趣可以自行测试。
还没完,虽然 .NET6 中关于 Blazor - 桌面应用的信息太少,但是我们可以看一下别的框架,例如 LiveSharp。
System.CommandLine
以往我们要执行命令,如在 Linux 上查看进程列表和资源消耗,会使用 top -b -n 1
,那么使用 C# 代码表示:
var psi = new ProcessStartInfo("top", "-b -n 1") { RedirectStandardOutput = true }; var proc = Process.Start(psi);
当然还有别的方法,但是似乎有点曲折,并且难以理解。在前面示例代码中,把 top 当作一个进程,然后使用参数启动,但是只适合那种一次性的程序。例如 top 相对于 Windows 中的任务管理器, -n 1
表示打印一次输出就结束,如果直接调用 top
则是一直动态变化,不会主动结束,这时就会导致出现问题。笔者意思是,这种方式不适合交互式的程序或命令行,如果被调用的程序一直不会结束,那么可能给代码带来问题;像 cat /etc/os-release
、ls -lah
这些命令,都是一次性输出的,很容易处理,不会带来问题。
还要另一种情况就是编写命令行程序。例如笔者就编写一个 dotnet tool 工具,名称为 csys,可以帮助查看主机的一些信息,用户可以输入命令加上参数,然后判断要使用的功能:
public static class Command { public const string Info = "info"; } Console.WriteLine("请输入命令"); //或者 Main(string[] args) 通过 args 获取 string command = ""; command = Console.ReadLine(); // netinfo if (command == Command.NETINFO) NETINFO();
这种方法太笨了,但是没有更加好的方法可以处理。
为了更加好地跨平台,.NET6 推出了 System.CommandLine 包,可以更加方便的执行命令和打造命令行程序。
这个库功能比较多,感兴趣的读者可参考:https://github.com/dotnet/command-line-api
通过这个库,我们可以很容易打造出像 wget、curl 这样的命令行程序。
例如,我们 编写了一个 myapp 的命令行程序,用户可以这样执行程序:
$> myapp [parse] --int-option not-an-int --file-option file.txt
要处理这些参数,我们使用 string[]
和 if
,似乎就不太灵活了。System.CommandLine
中有个 Option<T>
可以帮助程序员更加好地处理这些参数:
// Create a root command with some options var rootCommand = new RootCommand { new Option<int>( "--int-option", getDefaultValue: () => 42, description: "An option whose argument is parsed as an int"), new Option<bool>( "--bool-option", "An option whose argument is parsed as a bool"), new Option<FileInfo>( "--file-option", "An option whose argument is parsed as a FileInfo") };
例如 new Option<int>
,表示如果识别到有 --int-option
这个参数,那么后面带的值是数值类型,例如:
--int-option 123
getDefaultValue:() => 42
则设定一个默认值,如果用户启动命令时没有设置,则使用默认值。
如果没有填写这个参数,那么这次 Option<T>
将会使用默认值,例如:
# 不加任何参数 $> myapp
C# 处理这些参数的代码:
// Note that the parameters of the handler method are matched according to the names of the options rootCommand.Handler = CommandHandler.Create<int, bool, FileInfo>((intOption, boolOption, fileOption) => { Console.WriteLine($"The value for --int-option is: {intOption}"); Console.WriteLine($"The value for --bool-option is: {boolOption}"); Console.WriteLine($"The value for --file-option is: {fileOption?.FullName ?? "null"}"); });
输出结果:
The value for --int-option is: 42 The value for --bool-option is: False The value for --file-option is: null
示例代码或程序可参考:https://github.com/dotnet/command-line-api/blob/main/docs/Your-first-app-with-System-CommandLine.md
其它更新
其它一些更新主要是文字方面的,笔者将其中一些比较吸睛的信息整理了一下:
- Arm64
.NET5 对 Arm64 版本进行了大量的性能改进,.NET6 继续提升性能;
计划支持在 Windows Arm64 机器上运行 WPF;
提供了Apple Silicon(Arm64)芯片(本机和仿真)的支持; - 容器(Docker)
优化了容器中 .NET 程序的性能;
优化了官方镜像体积(PGO技术);
各种方式提升启动和吞吐量性能;
更新了基础镜像的版本; - System.Numerics
一组新的数学 API,提高处理数学的性能和根据硬件提升性能; - 改善单文件体积
发布的二进制文件打包形式可以提升体积大小;但是之前一直说的 AOT 极端优化,还没有出现; - Crossgen2
Roslyn 是 C# 中用于语法分析和编译 C# 代码的 API 集,可以将 C# 代码编译为 .dll;而 crossgen2 可以编译成本机代码而不是 .dll,crossgen2 是 C# 编写的,并且可以自举;crossgen2 仅适用于CoreCLR;
ASP.NET Core
ASP.NET Core 的路线图独立于 .NET,大家可以参考:https://themesof.net/ ,这个网站是 Blazor 编写的,不知道为什么,网页内容加载不出来,体验很不友好呀。也可以看这里获得一些路线计划:https://github.com/dotnet/aspnetcore/issues/27883
.NET6 主要计划内容:
- 热加载
开发中可以不需要重新编译,即可对运行中的程序更新 UI 和代码,写 Blazor 和 MVC、API 老方便了; - 微型 API
文档是这样说的:用更少的代码和仪式简化构建API端点。但是查看了 Issue 后,发现这个只是学习或者编写简单的 API 时可以减少没必要的 .dll,缩小体积; - 单文件发布
构建小型,独立的高性能应用程序和服务; - WebAssembly 提前(AoT)编译
发布时将 Blazor WebAssembly 应用程序中的.NET代码直接编译为 WebAssembly,以显着提高运行时性能;这样可以减少一些 .dll 文件; - SPA集成
不懂这个是什么,文档说可与最新的现代前端JavaScript框架无缝协作; - Blazor hybrid desktop apps
前面已经提到过,可以使用 Blazor 跟 MAUI 一起开发 UI 程序; - HTTP/3
支持 HTTP/3;提升支持力度;
下面说一下当前 .NET 6 Preview 1 更新的内容。
- IAsyncDisposable在MVC中的支持
现在IAsyncDisposable
接口,可以在控制器,页面模型和视图组件上实现以异步方式处置资源。 - DynamicComponent
DynamicComponent
是一个新的内置 Blazor 组件,可用于动态呈现按类型指定的组件。
<DynamicComponent Type="@someType" />
可以使用字典将参数传递给渲染的组件:
<DynamicComponent Type="@someType" Parameters="@myDictionaryOfParameters" /> @code { Type someType = ... IDictionary<string, object> myDictionaryOfParameters = ... }
- ElementReference
ElementReference 是一个对象,用于传递 HTML 元素引用,在 Js 中我们可以使用document.getElementById('someId')
来定位元素,但是在 Blazor 中许多组件动态组合,很难确定 ID 都是唯一的或者准确定位。为了解决这个问题,Blazor通过@ref
element标签 和ElementReference
struct 来处理。感兴趣的读者可以查看 Passing HTML element references。
现在 ElementReference 为 InputCheckbox,
InputDate,
InputFile,
InputNumber,
InputSelect,
InputText,和
InputTextArea 等输入组件提供了更加方便的处理方式,例如将 UI 焦点设置在这些输入组件上。
- 可空引用类型注释
这个是非常好的规范约束,现在 ASP.NET Core 中的各个部分都加上了这些注释,可以更加容易地提升项目的编译安全性。选择使用可为空的注释的项目可能会从ASP.NET Core API中看到新的生成时警告。
另外 EFCore 也进行了一些更新,没啥影响,这里就不提了。