前言:
眼前我们对“net开发触屏版软件”大概比较注意,我们都需要了解一些“net开发触屏版软件”的相关知识。那么小编也在网上搜集了一些有关“net开发触屏版软件””的相关知识,希望各位老铁们能喜欢,我们一起来学习一下吧!C# 是一种通用、类型安全、面向对象的编程语言。该语言的目标是程序员的生产力。为此,C# 平衡了简单性、表现力和性能。自第一个版本以来,该语言的首席架构师是Anders Hejlsberg(Turbo Pascal的创建者和Delphi的架构师)。C# 语言与平台无关,适用于一系列特定于平台的运行时。
面向对象
C# 是面向对象范例的丰富实现,其中包括、和。封装意味着在周围创建边界,以将其外部(公共)行为与其内部(私有)实现细节分开。从面向对象的角度来看,以下是 C# 的独特功能:
统一型系统
C# 中的基本构建基块是称为数据和函数的封装单元。C# 具有,其中所有类型最终共享一个公共基类型。这意味着所有类型,无论它们表示业务对象还是基元类型(如数字),都共享相同的基本功能。例如,任何类型的实例都可以通过调用其 ToString 方法转换为字符串。
类和接口
在传统的面向对象范式中,唯一的类型是类。在 C# 中,还有其他几种类型,其中一种是。接口就像一个无法保存数据的类。这意味着它只能定义(而不是),这允许多重继承以及规范和实现之间的分离。
属性、方法和事件
在纯面向对象的范式中,所有函数都是。在 C# 中,方法只是一种,它还包括和事件(还有其他属性和)。属性是封装对象状态片段(如按钮的颜色或标签的文本)的函数成员。事件是简化对对象状态更改的操作的函数成员。
尽管 C# 主要是一种面向对象的语言,但它也借鉴了函数式编程范;具体说来:
函数可以被视为值
使用,C# 允许函数作为值传递到其他函数或从其他函数传递。
C# 支持纯度模式
函数式编程的核心是避免使用值发生变化的变量,而支持声明性模式。C# 具有帮助处理这些模式的关键功能,包括能够动态编写“捕获”变量( 表达式)的未命名函数,以及通过执行列表或响应式编程的能力。C# 还提供,这使得编写(只读)类型变得容易。
类型安全
C# 主要是一种语言,这意味着类型的实例只能通过它们定义的协议进行交互,从而确保每个类型的内部一致性。例如,C# 阻止您与类型交互,就好像它是类型一样。
更具体地说,C# 支持静态类型,这意味着该语言在强制实施安全。这是对在强制实施的类型安全的补充。
静态类型甚至在程序运行之前就消除了一大类错误。它将负担从运行时单元测试转移到编译器上,以验证程序中的所有类型是否正确组合在一起。这使得大型程序更易于管理、更可预测且更健壮。此外,静态类型允许Visual Studio中的IntelliSense等工具帮助您编写程序,因为它知道给定变量的类型,因此可以对该变量调用哪些方法。此类工具还可以识别程序中使用变量、类型或方法的任何地方,从而实现可靠的重构。
注意
C# 还允许通过 dynamic 关键字动态键入部分代码。但是,C# 仍然是一种主要是静态类型的语言。
C# 也称为,因为它的类型规则是严格强制执行的(无论是静态还是在运行时)。例如,不能调用旨在接受具有浮点数的整数的函数,浮点数显式转换为整数。这有助于防止错误。
内存管理
C# 依赖于运行时来执行自动内存管理。公共语言运行库有一个垃圾回收器,该回收器作为程序的一部分执行,为不再引用的对象回收内存。这使程序员不必显式地为对象释放内存,从而消除了在C++等语言中遇到的错误指针的问题。
C# 不会消除指针:它只是使大多数编程任务不需要指针。对于性能关键型热点和互操作性,允许在标记为不安全的块中使用指针和显式内存分配。
平台支持
C# 具有支持以下平台的运行时:
Windows Desktop 7-11(适用于富客户端、Web、服务器和命令行应用程序)macOS(适用于富客户端、Web 和命令行应用程序)Linux 和 macOS(用于 Web 和命令行应用程序)安卓和iOS(用于移动应用程序)Windows 10 设备(Xbox、Surface Hub 和 HoloLens)
还有一种称为 的技术,可以将 C# 编译为在浏览器中运行的 Web 程序集。
CLR、BCL 和运行时
C# 程序的运行时支持包括和。运行时还可以包括更高级别的,其中包含用于开发胖客户端、移动或 Web 应用程序的库(参见)。存在不同的运行时以允许不同类型的应用程序以及不同的平台。
公共语言运行库
(CLR) 提供必要的运行时服务,如自动内存管理和异常处理。(“common”一词是指同一运行时可以由其他编程语言(如 F#、Visual Basic 和托管C++)共享的事实。
C# 之所以称为托管语言,是因为它将源代码编译为代码,托管代码以 (IL) 表示。CLR 将 IL 转换为计算机的本机代码,如 X64 或 X86,通常在执行之前。这称为实时 (JIT) 编译。提前编译也可用于缩短大型程序集或资源受限设备的启动时间(并在开发移动应用程序时满足 iOS 应用商店规则)。
托管代码的容器称为。程序集不仅包含 IL,还包含类型信息()。元数据的存在允许程序集引用其他程序集中的类型,而无需其他文件。
注意
您可以使用Microsoft的 工具检查和反汇编程序集的内容。使用ILSpy或JetBrain的dotPeek等工具,您可以更进一步将IL反编译为C#。由于 IL 比本机机器代码级别更高,因此反编译器可以很好地重建原始 C#。
程序可以查询自己的元数据(反射),甚至可以在运行时生成新的 IL()。
基类库
CLR 始终附带一组称为 (BCL) 的程序集。BCL 为程序员提供核心功能,例如集合、输入/输出、文本处理、XML/JSON 处理、网络、加密、互操作、并发和并行编程。
BCL 还实现 C# 语言本身所需的类型(用于枚举、查询和异步等功能),并允许您显式访问 CLR 的功能,如反射和内存管理。
运行时
(也称为)是下载和安装的可部署单元。运行时由 CLR(及其 BCL)以及特定于您正在编写的应用程序类型(Web、移动、富客户端等)的可选应用程序层组成(如果要编写命令行控制台或非 UI 库,则不需要应用程序层。
编写应用程序时,以特定运行时,这意味着应用程序使用并依赖于运行时提供的功能。运行时的选择还决定了应用程序将支持哪些平台。
下表列出了主要的运行时选项:
应用层
CLR/BCL
程序类型
运行在...
ASP.NET
.NET 6
蹼
Windows, Linux, macOS
视窗桌面
.NET 6
窗户
视窗 7-10+
毛伊岛(2022年初)
.NET 6
移动设备、桌面版
iOS, 安卓, 苹果操作系统, 视窗 10+
WinUI 3(2022 年初)
.NET 6
赢10
视窗 10+ 桌面
UWP
.NET Core 2.2
Win10 + Win10 设备
Windows 10+ 桌面和设备
(Legacy) .NET Framework
.NET 框架
网页, 视窗
视窗 7-10+
以图形方式显示了此信息,也可作为本书所涵盖内容的指南。
C 语言的运行时#.NET 6
.NET 6 是 Microsoft 的旗舰开源运行时。您可以编写在 Windows、Linux 和 macOS 上运行的 Web 和控制台应用程序,在 Windows 7 到 11 和 macOS 上运行的胖客户端应用程序,以及在 iOS 和 Android 上运行的移动应用程序。本书重点介绍.NET 6 CLR和BCL。
与.NET Framework不同,.NET 6没有预安装在Windows机器上。如果在不存在正确运行时的情况下尝试运行 .NET 6 应用程序,则会出现一条消息,将您定向到可在其中下载运行时的网页。可以通过创建部署来避免这种情况,该部署包括应用程序所需的运行时部分。
注意
.NET 6的前身是.NET 5,其前身是.NET Core 3。(Microsoft从名称中删除了“核心”,并跳过了版本 4)。跳过版本的原因是为了避免与 4.x混淆。
这意味着在大多数情况下,在 .NET Core 版本 1、2 和 3(以及 .NET 5)下编译的程序集将在 .NET 6 下无需修改即可运行。相反,在(任何版本的).NET Framework 下编译的程序集通常与 .NET 6 不兼容。
.NET 6 BCL和CLR与.NET 5(和.NET Core 3)非常相似,其区别主要集中在性能和部署上。
毛 伊 岛
(多平台应用程序 UI,2022 年初)旨在创建适用于 iOS 和 Android 的移动应用程序,以及适用于 macOS 和 Windows 的跨平台桌面应用程序。MAUI 是 Xamarin 的演变,允许单个项目面向多个平台。
UWP 和 WinUI 3
(UWP) 旨在编写在 Windows 10+ 桌面和设备(Xbox、Surface Hub 和 HoloLens)上运行的沉浸式触摸优先应用程序。UWP 应用经过沙盒处理,并通过 Windows 应用商店提供。UWP 预装在 Windows 10 中。它使用 .NET Core 2.2 CLR/BCL 的版本,并且不太可能更新此依赖项。相反,Microsoft发布了一个名为的继任者,作为的一部分。
Windows 应用 SDK 适用于最新的 .NET,与 .NET 桌面 API 更好地集成,并且可以在沙盒外部运行。但是,它尚不支持Xbox或HoloLens等设备。
.NET 框架
是 Microsoft 最初的仅限 Windows 的运行时,用于编写(仅)在 Windows 桌面/服务器上运行的 Web 和富客户端应用程序。没有计划发布重大的新版本,但由于现有应用程序的丰富性,Microsoft将继续支持和维护当前的 4.8 版本。
在.NET Framework中,CLR/BCL与应用程序层集成在一起。用 .NET Framework 编写的应用程序可以在 .NET 6 下重新编译,尽管它们通常需要一些修改。.NET Framework 的某些功能在 .NET 6 中不存在(反之亦然)。
.NET Framework 预装在 Windows 中,并通过 Windows Update 自动修补。当您面向 .NET Framework 4.8 时,可以使用 C# 7.3 及更早版本的功能。
注意
长期以来,“.NET”一词一直被用作包含“.NET”一词的任何技术(.NET Framework,.NET Core,.NET Standard等)的总称。
这意味着Microsoft将.NET Core重命名为.NET造成了不幸的歧义。在本书中,我们将新的.NET称为。为了提到.NET Core及其后续版本,我们将使用短语“.NET Core和.NET 5+”。
更令人困惑的是,.NET(5+)是一个框架,但它与非常不同。因此,在可能的情况下,我们将优先使用术语而不是。
利基运行时
还有以下利基运行时:
.NET Micro Framework 用于在资源高度受限的嵌入式设备(小于 1 MB)上运行 .NET 代码。Unity 是一个游戏开发平台,允许使用 C# 编写游戏逻辑脚本。
也可以在 SQL Server 中运行托管代码。通过 SQL Server CLR 集成,可以使用 C# 编写自定义函数、存储过程和聚合,然后从 SQL 调用它们。它与 .NET Framework 和特殊的“托管”CLR 结合使用,后者强制实施沙箱以保护 SQL Server 进程的完整性。
C语言简史#
以下是每个 C# 版本中新功能的反向时间顺序,以便已经熟悉该语言旧版本的读者受益。
C# 10 中的新增功能
C# 10 随 Visual Studio 2022 一起提供,并在面向 .NET 6 时使用。
文件范围的命名空间
在文件中所有类型都在单个命名空间中定义的常见情况下,C# 10 中的声明可减少混乱并消除不必要的缩进级别:
namespace MyNamespace; // Applies to everything that follows in the file.class Class1 {} // inside MyNamespaceclass Class2 {} // inside MyNamespace全局使用指令
在 using 指令前面加上 global 关键字时,它会将该指令应用于项目中的所有文件:
global using System;global using System.Collection.Generic;
这样可以避免在每个文件中重复相同的指令。全局 using 指令与 using static .
此外,.NET 6 项目现在支持:如果在项目文件中将 ImplicitUsings 元素设置为 true,则会自动导入最常用的命名空间(基于 SDK 项目类型)。有关更多详细信息,请参阅中的
匿名类型的非破坏性突变
C# 9 引入了 with 关键字,用于对记录执行非破坏性更改。在 C# 10 中,with 关键字也适用于匿名类型:
var a1 = new { A = 1, B = 2, C = 3, D = 4, E = 5 };var a2 = a1 with { E = 10 }; Console.WriteLine (a2); // { A = 1, B = 2, C = 3, D = 4, E = 10 }新的解构语法
C# 7 引入了元组(或使用解构方法的任何类型的元组)的解构语法。C# 10 更进一步扩展了此语法,允许您在同一解构中混合赋值和声明:
var point = (3, 4);double x = 0;(x, double y) = point;结构中的字段初始值设定项和无参数构造函数
从 C# 10 开始,可以在结构中包含字段初始值设定项和无参数构造函数(请参阅中的)。这些仅在显式调用构造函数时执行,因此可以轻松绕过 - 例如,通过默认关键字。引入此功能主要是为了结构记录。
记录结构
记录最初是在 C# 9 中引入的,它们充当编译增强类。在 C# 10 中,记录也可以是结构:
record struct Point (int X, int Y);
规则在其他方面是相似的:记录结构与具有大致相同的功能(请参阅中的)。 例外情况是,编译器在记录结构上生成的属性是可写的,除非您在记录声明前面加上 readonly 关键字。
Lambda 表达式增强功能
围绕 lambda 表达式的语法已通过多种方式得到增强。首先,允许隐式类型 ( var ):
var greeter = () => "Hello, world";
lambda 表达式的隐式类型是 Action 或 Func 委托,因此在这种情况下,greeter 的类型为 Func<string> 。必须显式声明任何参数类型:
var square = (int x) => x * x;
其次,lambda 表达式可以指定返回类型:
var sqr = int (int x) => x;
这主要是为了提高复杂嵌套 lambda 的编译器性能。
第三,您可以将 lambda 表达式传递到对象、委托或表达式类型的方法参数中:
M1 (() => "test"); // Implicitly typed to Func<string>M2 (() => "test"); // Implicitly typed to Func<string>M3 (() => "test"); // Implicitly typed to Expression<Func<string>>void M1 (object x) {}void M2 (Delegate x) {}void M3 (Expression x) {}
最后,您可以将属性应用于 lambda 表达式的编译生成的目标方法(及其参数和返回值):
Action a = [Description("test")] () => { };
有关更多详细信息,请参阅中的
嵌套属性模式
以下简化语法在 C# 10 中对于嵌套属性模式匹配是合法的(请参阅中的):
var obj = new Uri (";);if (obj is Uri { Scheme.Length: 5 }) ...
这相当于:
if (obj is Uri { Scheme: { Length: 5 }}) ...调用者参数表达式
应用 [CallerArgumentExpression] 属性的方法参数从调用站点捕获参数表达式:
Print (Math.PI * 2);void Print (double number, [CallerArgumentExpression("number")] string expr = null) => Console.WriteLine (expr);// Output: Math.PI * 2
此功能主要用于验证和断言库(请参阅中的)。
其他新功能
#line 指令已在 C# 10 中得到增强,允许指定列和范围。
C# 10 中的内插字符串可以是常量,只要内插的值是常量即可。
记录可以在 C# 10 中密封 ToString() 方法。
C# 的定赋值分析已得到改进,因此表达式如下:
if (foo?.TryParse ("123", out var number) ?? false) Console.WriteLine (number);
(在 C# 10 之前,编译器将生成错误:“使用未赋值的局部变量'number'。
C# 9.0 中的新增功能
C# 9.0 随 一起提供,并在面向 .NET 5 时使用。
顶级语句
使用(参见中的),您可以编写一个没有 Main 方法和 Program 类包袱的程序:
using System;Console.WriteLine ("Hello, world");
顶级语句可以包含方法(充当本地方法)。您还可以通过 “magic” args 变量访问命令行参数,并向调用方返回一个值。顶级语句后可以跟类型和命名空间声明。
仅初始化设置器
属性声明中的仅(参见中的使用 init 关键字而不是 set 关键字:
class Foo { public int ID { get; init; } }
这类似于只读属性,只是它也可以通过对象初始值设定项进行设置:
var foo = new Foo { ID = 123 };
这使得创建可通过对象初始值设定项而不是构造函数填充的不可变(只读)类型成为可能,并有助于避免接受大量可选参数的构造函数的反模式。仅初始化设置器在中使用时还允许。
记录
(参见中的是一种特殊的类,旨在很好地处理不可变数据。它最特别的特点是关键字(带有):
Point p1 = new Point (2, 3);Point p2 = p1 with { Y = 4 }; // p2 is a copy of p1, but with Y set to 4Console.WriteLine (p2); // Point { X = 2, Y = 4 }record Point{ public Point (double x, double y) => (X, Y) = (x, y); public double X { get; init; } public double Y { get; init; } }
在简单情况下,记录还可以消除定义属性以及编写构造函数和解构函数的样板代码。我们可以将点记录定义替换为以下内容,而不会丢失功能:
record Point (double X, double Y);
与元组一样,默认情况下,记录表现出结构相等性。记录可以对其他记录进行子类化,并且可以包含类可以包含的相同构造。编译器在运行时将记录实现为类。
模式匹配改进
(参见中的允许 <、>、<= 和 >= 运算符出现在模式中:
string GetWeightCategory (decimal bmi) => bmi switch { < 18.5m => "underweight", < 25m => "normal", < 30m => "overweight", _ => "obese" };
使用,您可以通过三个新关键字( 和 , or 和 not )组合模式:
bool IsVowel (char c) => c is 'a' or 'e' or 'i' or 'o' or 'u';bool IsLetter (char c) => c is >= 'a' and <= 'z' or >= 'A' and <= 'Z';
与 && 和 || 一样运算符,并且具有高于 或 的优先级。您可以使用括号覆盖此设置。
not 组合器可以与类型模式一起使用,以测试对象是否是(不是):
if (obj is not string) ...目标类型的新表达式
构造对象时,C# 9 允许在编译器可以明确推断类型名称时省略类型名称:
System.Text.StringBuilder sb1 = new();System.Text.StringBuilder sb2 = new ("Test");
当变量声明和初始化位于代码的不同部分时,这特别有用:
class Foo{ System.Text.StringBuilder sb; public Foo (string initialValue) => sb = new (initialValue);}
在以下情况下:
MyMethod (new ("test"));void MyMethod (System.Text.StringBuilder sb) { ... }
有关详细信息,请参阅中的
互操作改进
C# 9 引入了函数指针(请参阅 章中的“函数指针”和第 中的)。 它们的主要用途是允许非托管代码在 C# 中调用静态方法,而无需委托实例的开销,并且能够在参数和返回类型可 (在每一端以相同方式表示)时绕过 P/Invoke 层。
C# 9 还引入了 nint 和 nuint 本机大小的整数类型(请参阅中的),这些类型在运行时映射到 System.IntPtr 和 System.UIntPtr 。在编译时,它们的行为类似于支持算术运算的数字类型。
其他新功能
此外,C# 9 现在允许你:
重写方法或只读属性,使其返回派生类型(请参阅中的)将属性应用于本地函数(请参阅中的)将 static 关键字应用于 lambda 表达式或局部函数,以确保不会意外捕获局部变量或实例变量(请参阅中的)通过编写 GetEnumerator 扩展方法,使任何类型都使用 foreach 语句定义在首次加载程序集时执行一次的方法,方法是将 [ModuleInitializer] 属性应用于(静态无参数)方法使用“丢弃”(下划线符号)作为 lambda 表达式参数编写必须实现的方法 - 启用 Roslyn 的新等方案(请参阅中的)将属性应用于方法、类型或模块,以防止运行时初始化局部变量(请参阅中的)C# 8.0 中的新增功能
C# 8.0 首次随 一起提供,今天在面向 .NET Core 3 或 .NET Standard 2.1 时仍在使用。
指数和范围
简化了数组的元素或部分(或低级类型 Span<T> 和 ReadOnlySpan<T> )的使用。
索引允许您使用 ^ 运算符引用相对于数组的元素。^1 表示最后一个元素,^2 表示倒数第二个元素,依此类推:
char[] vowels = new char[] {'a','e','i','o','u'};char lastElement = vowels [^1]; // 'u'char secondToLast = vowels [^2]; // 'o'
范围允许您使用 ..算子:
char[] firstTwo = vowels [..2]; // 'a', 'e'char[] lastThree = vowels [2..]; // 'i', 'o', 'u'char[] middleOne = vowels [2..3] // 'i'char[] lastTwo = vowels [^2..]; // 'o', 'u'
C# 借助索引和范围类型实现索引和范围:
Index last = ^1;Range firstTwoRange = 0..2;char[] firstTwo = vowels [firstTwoRange]; // 'a', 'e'
可以通过定义参数类型为 Index 或 Range 的索引器来支持自己的类中的索引和范围:
class Sentence{ string[] words = "The quick brown fox".Split(); public string this [Index index] => words [index]; public string[] this [Range range] => words [range];}
有关更多信息,请参阅中的
零合并分配
这??= 运算符仅在变量为 null 时才分配变量。而不是
if (s == null) s = "Hello, world";
您现在可以这样写:
s ??= "Hello, world";使用声明
如果省略 using 语句后面的方括号和语句块,则它将成为 。然后,当执行落在语句块之外时,将释放资源:
if (File.Exists ("file.txt")){ using var reader = File.OpenText ("file.txt"); Console.WriteLine (reader.ReadLine()); ...}
在这种情况下,当执行落在 if 语句块之外时,将释放读取器。
只读成员
C# 8 允许您将只读修饰符应用于结构的函数,确保如果尝试修改任何字段,则会生成编译时错误:
struct Point{ public int X, Y; public readonly void ResetX() => X = 0; // Error!}
如果只读函数调用非只读函数,编译器将生成警告(并防御性地复制结构以避免发生突变的可能性)。
静态本地方法
将 static 修饰符添加到局部方法可防止它看到封闭方法的局部变量和参数。这有助于减少耦合,并使本地方法能够随心所欲地声明变量,而不会有与包含方法中的变量发生冲突的风险。
默认接口成员
C# 8 允许您向接口成员添加默认实现,使其成为可选的实现:
interface ILogger{ void Log (string text) => Console.WriteLine (text);}
这意味着您可以在不中断实现的情况下向接口添加成员。默认实现必须通过接口显式调用:
((ILogger)new Logger()).Log ("message");
接口还可以定义静态成员(包括字段),这些成员可以从默认实现中的代码访问:
interface ILogger{ void Log (string text) => Console.WriteLine (Prefix + text); static string Prefix = ""; }
或从接口外部,除非通过静态接口成员(例如私有、受保护或内部)上的可访问性修饰符进行限制:
ILogger.Prefix = "File log: ";
禁止使用实例字段。有关更多详细信息,请参阅中的
切换表达式
从 C# 8 开始,可以在的上下文中使用 switch:
string cardName = cardNumber switch // assuming cardNumber is an int{ 13 => "King", 12 => "Queen", 11 => "Jack", _ => "Pip card" // equivalent to 'default'};
有关更多示例,请参阅中的
元组、位置和属性模式
C# 8 支持三种新模式,主要是为了 switch 语句/表达式(请参阅中的)。允许您打开多个值:
int cardNumber = 12; string suite = "spades";string cardName = (cardNumber, suite) switch{ (13, "spades") => "King of spades", (13, "clubs") => "King of clubs", ...};
模式允许对公开解构函数的对象使用类似的语法,允许您匹配对象的属性。您可以在开关中和 is 运算符中使用所有模式。下面的示例使用来测试 obj 是否为长度为 4 的字符串:
if (obj is string { Length:4 }) ...可为空的引用类型
可为 null 类型为值类型带来可为空性,而 的引用类型则相反,为引用类型带来(一定程度的),目的是帮助避免 NullReferenceExceptions。可为 null 的引用类型引入了一种安全级别,当编译器检测到存在生成 NullReferenceException 风险的代码时,该级别纯粹由编译器以警告或错误的形式强制实施。
可以在项目级别(通过 项目文件中的 Nullable 元素)或在代码(通过 #nullable 指令)启用可为 null 的引用类型。启用后,编译器会将非可为空性设为默认值:如果希望引用类型接受 null,则必须应用 ?指示后缀:
#nullable enable // Enable nullable reference types from this point onstring s1 = null; // Generates a compiler warning! (s1 is non-nullable)string? s2 = null; // OK: s2 is nullable reference type
未初始化的字段也会生成警告(如果类型未标记为可为空),如果编译器认为可能发生 NullReferenceException,则取消引用可为空的引用类型也是如此:
void Foo (string? s) => Console.Write (s.Length); // Warning (.Length)
若要删除警告,可以使用 ( !
void Foo (string? s) => Console.Write (s!.Length);
有关完整讨论,请参阅中的
异步流
在 C# 8 之前,可以使用 yield return 编写,或等待编写。但是你不能同时做这两件事,并编写一个等待的迭代器,异步生成元素。C# 8 通过引入来解决此问题:
async IAsyncEnumerable<int> RangeAsync ( int start, int count, int delay){ for (int i = start; i < start + count; i++) { await Task.Delay (delay); yield return i; }}
await foreach 语句使用异步流:
await foreach (var number in RangeAsync (0, 10, 100)) Console.WriteLine (number);
有关详细信息,请参阅中的
C# 7.x 中的新增功能
C# 7.x 最初随 Visual Studio 2017 一起提供。今天,当您面向.NET Core 7,.NET Framework 3.2019至2.4或.NET Standard 6.4时,Visual Studio 8仍在使用C# 2.0。
C# 7.3
C# 7.3 对现有功能进行了细微改进,例如允许将相等运算符与元组一起使用、改进重载分辨率以及将特性应用于自动属性的支持字段的功能:
[field:NonSerialized]public int MyProperty { get; set; }
C# 7.3 还基于 C# 7.2 的高级低分配编程功能构建,能够重新分配 ,在索引固定字段时无需固定,并且使用 stackalloc 支持字段初始值设定项:
int* pointer = stackalloc int[] {1, 2, 3};Span<int> arr = stackalloc [] {1, 2, 3};
请注意,堆栈分配的内存可以直接分配给 Span<T> 。我们将在第 中描述跨度,以及为什么要使用它们。
C# 7.2
C# 7.2 添加了一个新的私有受保护修饰符(内部修饰符和受保护修饰符的),在调用方法时跟随带有位置参数的命名参数的功能,以及只读结构。只读结构强制所有字段都是只读的,以帮助声明意图并允许编译器更大的优化自由度:
readonly struct Point{ public readonly int X, Y; // X and Y must be readonly}
C# 7.2 还添加了专门的功能来帮助进行微优化和低分配编程:请参阅 章中的“引用”和“,以及中的
C# 7.1
从 C# 7.1 开始,如果可以推断出类型,则可以在使用默认关键字时省略该类型:
decimal number = default; // number is decimal
C# 7.1 还放宽了 switch 语句的规则(以便可以在泛型类型参数上进行模式匹配),允许程序的 Main 方法是异步的,并允许推断元组元素名称:
var now = DateTime.Now;var tuple = (now.Hour, now.Minute, now.Second);数字文字改进
C# 7 中的数字文本可以包含下划线以提高可读性。这些称为数字分隔符,编译器会忽略这些:
int million = 1_000_000;
可以使用 0b 前缀指定:
var b = 0b1010_1011_1100_1101_1110_1111;输出变量和丢弃
C# 7 使调用包含 out 参数的方法变得更加容易。首先,您现在可以动态声明(请参阅中的):
bool successful = int.TryParse ("123", out int result);Console.WriteLine (result);
当调用具有多个 out 参数的方法时划线字符丢弃您不感兴趣的方法:
SomeBigMethod (out _, out _, out _, out int x, out _, out _, out _);Console.WriteLine (x);类型模式和模式变量
您还可以使用 is 运算符动态引入变量。这些被称为模式变量(参见中的):
void Foo (object x){ if (x is string s) Console.WriteLine (s.Length);}
switch 语句还支持类型模式,因此您可以打开常量(请参阅中的)。您可以使用 when 子句指定条件,也可以打开空值:
switch (x){ case int i: Console.WriteLine ("It's an int!"); break; case string s: Console.WriteLine (s.Length); // We can use the s variable break; case bool b when b == true: // Matches only when b is true Console.WriteLine ("True"); break; case null: Console.WriteLine ("Nothing"); break;}本地方法
在另一个函数中声明的方法(请参阅中的):
void WriteCubes(){ Console.WriteLine (Cube (3)); Console.WriteLine (Cube (4)); Console.WriteLine (Cube (5)); int Cube (int value) => value * value * value;}
局部方法仅对包含函数可见,并且可以像 lambda 表达式一样捕获局部变量。
更多善于表达的成员
C# 6 为方法、只读属性、运算符和索引器引入了表达式体“fat-arrow”语法。C# 7 将其扩展到构造函数、读/写属性和终结器:
public class Person{ string name; public Person (string name) => Name = name; public string Name { get => name; set => name = value ?? ""; } ~Person () => Console.WriteLine ("finalize");}解构函数
C# 7 引入了解构模式(请参阅中的)。构造函数通常采用一组值(作为参数)并将它们分配给字段,而则执行相反的操作,并将字段分配回一组变量。我们可以在前面的示例中为 Person 类编写一个解构函数,如下所示(除了异常处理):
public void Deconstruct (out string firstName, out string lastName){ int spacePos = name.IndexOf (' '); firstName = name.Substring (0, spacePos); lastName = name.Substring (spacePos + 1);}
解构函数使用以下特殊语法调用:
var joe = new Person ("Joe Bloggs");var (first, last) = joe; // DeconstructionConsole.WriteLine (first); // JoeConsole.WriteLine (last); // Bloggs元组
也许对 C# 7 最显着的改进是显式支持(请参阅中的)。元组提供了一种存储一组相关值的简单方法:
var bob = ("Bob", 23);Console.WriteLine (bob.Item1); // BobConsole.WriteLine (bob.Item2); // 23
C# 的新元组是使用 System.ValueTuple<...>泛型结构。但是多亏了编译器的魔力,元组元素可以命名为:
var tuple = (name:"Bob", age:23);Console.WriteLine (tuple.name); // BobConsole.WriteLine (tuple.age); // 23
使用元组,函数可以返回多个值,而无需求助于 out 参数或额外的类型包袱:
static (int row, int column) GetFilePosition() => (3, 10);static void Main(){ var pos = GetFilePosition(); Console.WriteLine (pos.row); // 3 Console.WriteLine (pos.column); // 10}
元组隐式支持解构模式,因此您可以轻松地将它们为单个变量:
static void Main(){ (int row, int column) = GetFilePosition(); // Creates 2 local variables Console.WriteLine (row); // 3 Console.WriteLine (column); // 10}抛出表达式
在 C# 7 之前,throw 始终是一个语句。现在,它也可以在表达式体函数中显示为表达式:
public string Foo() => throw new NotImplementedException();
抛出表达式也可以出现在三元条件表达式中:
string Capitalize (string value) => value == null ? throw new ArgumentException ("value") : value == "" ? "" : char.ToUpper (value[0]) + value.Substring (1);C# 6.0 中的新增功能
附带的C# 0.2015具有新一代编译器,完全用C#编写。新的编译器称为项目“Roslyn”,通过库公开整个编译管道,允许您对任意源代码执行代码分析。编译器本身是开源的,源代码可在 获得。
此外,C# 6.0 还具有几个次要但重要的增强功能,主要旨在减少代码混乱。
(“Elvis”)运算符(参见中的避免了在调用方法或访问类型成员之前显式检查null。在下面的示例中,结果计算结果为 null,而不是抛出 NullReferenceException:
System.Text.StringBuilder sb = null;string result = sb?.ToString(); // result is null
(参见中的允许以 lambda 表达式的样式更简洁地编写组成单个表达式的方法、属性、运算符和索引器:
public int TimesTwo (int x) => x * 2;public string SomeProperty => "Property value";
()允许您为自动属性分配初始值:
public DateTime TimeCreated { get; set; } = DateTime.Now;
初始化的属性也可以是只读的:
public DateTime TimeCreated { get; } = DateTime.Now;
还可以在构造函数中设置只读属性,从而更轻松地创建不可变(只读)类型。
()允许公开索引器的任何类型的单步初始化:
var dict = new Dictionary<int,string>(){ [3] = "three", [10] = "ten"};
字符串(参见中的的简洁替代方法。格式:
string s = $"It is {DateTime.Now.DayOfWeek} today";
(参见中的允许您将条件应用于 catch 块:
string html;try{ html = await new HttpClient().GetStringAsync (";);}catch (WebException ex) when (ex.Status == WebExceptionStatus.Timeout){ ...}
using static(参见中的指令允许您导入类型的所有静态成员,以便您可以使用这些非限定成员:
using static System.Console;...WriteLine ("Hello, world"); // WriteLine instead of Console.WriteLine
nameof()运算符以字符串形式返回变量、类型或其他符号的名称。这样可以避免在 Visual Studio 中重命名符号时中断代码:
int capacity = 123;string x = nameof (capacity); // x is "capacity"string y = nameof (Uri.Host); // y is "Host"
最后,您现在可以等待内部捕获并最终阻止。
C# 5.0 中的新增功能
C# 5.0 的一大新功能是通过两个新关键字 async 和 await 支持。异步函数支持,这使得编写响应式和线程安全的胖客户端应用程序变得更加容易。它们还可以轻松编写高并发且高效的 I/O 绑定应用程序,这些应用程序不会占用每个操作的线程资源。我们将在第 中详细介绍异步函数。
C# 4.0 中的新增功能
C# 4.0 引入了四个主要增强功能:
( 章和第 19 章)将(解析类型和成员的过程)从编译时推迟到运行时,并且在需要复杂反射代码的方案中非常有用。动态绑定在与动态语言和 COM 组件进行互操作时也很有用。
()允许函数指定默认参数值,以便调用方可以省略参数,参数允许函数调用方按名称而不是位置标识参数。
在 C# 4.0(和第 )中放宽了类型规则,以便泛型接口和泛型委托中的类型参数可以标记为变或,从而允许更自然的类型转换。
()在 C# 4.0 中以三种方式得到增强。首先,参数可以在没有 ref 关键字的情况下通过引用传递(与可选参数结合使用特别有用)。其次,可以而不是包含 COM 互操作类型的程序集。链接互操作类型支持类型等效性,避免了的需求,并结束了版本控制和部署难题。第三,从链接互操作类型返回 COM-Variant 类型的函数被映射到动态而不是对象,从而消除了强制转换的需要。
C# 3.0 中的新增功能
添加到 C# 3.0 的功能主要集中在 (LINQ) 功能上。LINQ 允许直接在 C# 程序中编写查询并检查其正确性,并查询本地集合(如列表或 XML 文档)或远程数据源(如数据库)。为支持 LINQ 而添加的 C# 3.0 功能包括隐式类型化局部变量、匿名类型、对象初始值设定项、lambda 表达式、扩展方法、查询表达式和表达式树。
变量(var关键字,)允许您在声明语句中省略变量类型,从而允许编译器推断它。这减少了混乱,并允许(),匿名类型是动态创建的简单类,通常用于 LINQ 查询的最终输出。您还可以隐式类型化数组()。
()允许您在构造函数调用后以内联方式设置属性,从而简化了对象构造。对象初始值设定项同时适用于命名类型和匿名类型。
()是由编译器动态创建的微型函数;它们在“流畅”的 LINQ 查询中特别有用()。
方法()使用新方法扩展现有类型(不更改类型的定义),使静态方法感觉像实例方法。LINQ 的查询运算符作为扩展方法实现。
()为编写 LINQ 查询提供了更高级别的语法,在处理多个序列或范围变量时,该语法可以简单得多。
()是微型代码文档对象模型 (DOM),用于描述分配给特殊类型 Expression<TDelegate> 的 lambda 表达式。表达式树使 LINQ 查询可以远程执行(例如,在数据库服务器上),因为它们可以在运行时进行内省和转换(例如,转换为 SQL 语句)。
C# 3.0 还添加了自动属性和分部方法。
()通过让编译器自动执行该工作来减少编写属性的工作,这些属性只是获取/设置私有支持字段。()让自动生成的分部类为手动创作提供可自定义的钩子,如果不使用,这些钩子就会“消失”。
C# 2.0 中的新增功能
C# 2 中的重大新功能是泛型(第 章)、可为空的值类型( 章)、迭代器()和匿名方法(lambda 表达式的前身)。这些功能为在 C# 3 中引入 LINQ 铺平了道路。
C# 2 还添加了对分部类、静态类以及大量次要和杂项功能(如命名空间别名限定符、友元程序集和固定大小缓冲区)的支持。
泛型的引入需要新的 CLR (CLR 2.0),因为泛型在运行时保持完整类型保真度。
标签: #net开发触屏版软件 #js运算符中和的区别在哪里 #winccnet变量 #netnonserialized