Visual Studio 2022公告
全局和隐式 usings
using 指令简化了你使用命名空间的方式C# 10 包括一个新的全局 using 指令和隐式 usings,以减少你需要在每个文件顶部指定的 usings 数量
全局 using 指令
如果关键字global出现在using指令之前,则using适用于整个项目:
globalusingSystem,
你可以在全局 using 指令中使用 using 的任何功能例如,添加静态导入类型并使该类型的成员和嵌套类型在整个项目中可用
globalusingstaticSystem.Console,globalusingEnv=System.Environment,
你可以将全局使用放在任何 .cs 文件中,包括 Program.cs 或专门命名的文件,如 globalusings.cs全局 usings 的范围是当前编译,一般对应当前项目
有关详细信息,请参阅全局 using 指令。
全局using指令
隐式 usings
隐式 usings 功能会自动为你正在构建的项目类型添加通用的全局 using 指令。要启用隐式 usings,请在 .csproj 文件中设置 ImplicitUsings 属性:
lt,PropertyGroupgt,lt,!——OtherpropertieslikeOutputTypeandTargetFramework——gt,lt,ImplicitUsingsgt,enablelt,/ImplicitUsingsgt,lt,/PropertyGroupgt,
有关详细信息,请参阅此隐式 usings 文章。
博客文章
隐式 usings
Combiningusing 功能
无论它们是如何定义的,额外的 using 指令都会增加名称解析中出现歧义的可能性如果遇到这种情况,请考虑添加别名或减少要导入的命名空间的数量例如,你可以将全局 using 指令替换为文件子集顶部的显式 using 指令
如果你需要删除通过隐式 usings 包含的命名空间,你可以在项目文件中指定它们:
lt,ItemGroupgt,lt,UsingRemove="System.Threading.Tasks"/gt,lt,/ItemGroupgt,
你还可以添加命名空间,就像它们是全局 using 指令一样,你可以将 Using 项添加到项目文件中,例如:
lt,ItemGroupgt,lt,UsingInclude="System.IO.Pipes"/gt,lt,/ItemGroupgt,文件范围的命名空间
许多文件包含单个命名空间的代码。从 C# 10 开始,你可以将命名空间作为语句包含在内,后跟分号且不带花括号:
namespaceMyCompany.MyNamespace,classMyClass//Note:noindentation...
他简化了代码并删除了嵌套级别只允许一个文件范围的命名空间声明,并且它必须在声明任何类型之前出现
有关文件范围命名空间的更多信息,请参阅命名空间关键字文章。
对lambda 表达式和方法组的改进
lambda的语法
lambda的自然类型
Lambda 表达式现在有时具有自然类型这意味着编译器通常可以推断出 lambda 表达式的类型
到目前为止,必须将 lambda 表达式转换为委托或表达式类型。在大多数情况下,你会在 BCL 中使用重载的 Funclt,...gt, 或 Actionlt,...gt, 委托类型之一:
Funclt,string,intgt,parse==gt,int.Parse,
但是,从 C# 10 开始,如果 lambda 没有这样的目标类型,微软将尝试为你计算一个:
varparse==gt,int.Parse,
并非所有 lambda 表达式都有自然类型 —— 有些只是没有足够的类型信息。例如,放弃参数类型将使编译器无法决定使用哪种委托类型:
varparse=s=gt,int.Parse,//ERROR:Notenoughtypeinfointhelambda
lambda 的自然类型意味着它们可以分配给较弱的类型,例如 object 或 Delegate:
objectparse==gt,int.Parse,//Funclt,string,intgt,Delegateparse==gt,int.Parse,//Funclt,string,intgt,
当涉及到表达式树时,微软结合了目标和自然类型。如果目标类型是 LambdaExpression 或非泛型 Expression并且 lambda 具有自然委托类型 D,微软将改为生成 Expressionlt,Dgt,:
LambdaExpressionparseExpr==gt,int.Parse,//Expressionlt,Funclt,string,intgt,gt,ExpressionparseExpr==gt,int.Parse,//Expressionlt,Funclt,string,intgt,gt,
方法组的自然类型
方法组现在有时也具有自然类型。你始终能够将方法组转换为兼容的委托类型:
Funclt,intgt,read=Console.Read,Actionlt,stringgt,write=Console.Write,
现在,如果方法组只有一个重载,它将具有自然类型:
varread=Console.Read,//Justoneoverload,Funclt,intgt,inferredvarwrite=Console.Write,//ERROR:Multipleoverloads,can'tchoose
lambda的返回类型
在前面的示例中,lambda 表达式的返回类型是显而易见的,并被推断出来的。情况并非总是如此:
varchoose==gt,b。1:"two",//ERROR:Can'tinferreturntype
在 C# 10 中,你可以在 lambda 表达式上指定显式返回类型,就像在方法或本地函数上一样返回类型在参数之前
varchoose=object=gt,b。1:"two",//Funclt,bool,objectgt,
lambda 上的属性
从 C# 10 开始,你可以将属性放在 lambda 表达式上,就像对方法和本地函数一样。醋酸:醋酸近期又开启了新一轮的下跌,下游部分行业在限电放松后虽然开工恢复正常水平,出口环比也有一定提升,但是供给端恢复同样较大,考虑终端在冬季需求转淡,对于原料醋酸采买理性为主,新单成交有限。当有属性时,lambda 的参数列表必须用括号括起来:
Funclt,string,intgt,parse=)=gt,int.Parse,varchoose=(Example(2))(Example(3))object(boolb)=gt,b。1:"two",
就像本地函数一样,如果属性在 AttributeTargets.Method 上有效,则可以将属性应用于 lambda。
Lambda 的调用方式与方法和本地函数不同,因此在调用 lambda 时属性没有任何影响但是,lambdas 上的属性对于代码分析仍然有用,并且可以通过反射发现它们
structs的改进
C# 10 为 structs 引入了功能,可在 structs 和类之间提供更好的奇偶性这些新功能包括无参数构造函数,字段初始值设定项,记录结构和 with 表达式
01 无参数结构构造函数和字段初始值设定项
在 C# 10 之前,每个结构都有一个隐式的公共无参数构造函数,该构造函数将结构的字段设置为默认值在结构上创建无参数构造函数是错误的
从 C# 10 开始,你可以包含自己的无参数结构构造函数如果你不提供,则将提供隐式无参数构造函数以将所有字段设置为默认值
publicstructAddresspublicAddressCity="lt,unknowngt,",publicstringCityget,init,
你可以如上所述在无参数构造函数中初始化字段,也可以通过字段或属性初始化程序初始化它们:
publicstructAddresspublicstringCityget,init,="lt,unknowngt,",
通过默认创建或作为数组分配的一部分创建的结构会忽略显式无参数构造函数,并始终将结构成员设置为其默认值有关结构中无参数构造函数的更多信息,请参阅结构类型
02 Record structs
从 C# 10 开始,现在可以使用 record struct 定义 record。这些类似于 C# 9 中引入的 record 类:
publicrecordstructPersonpublicstringFirstNameget,init,publicstringLastNameget,init,
你可以继续使用 record 定义记录类,也可以使用 record 类来清楚地说明。
结构已经具有值相等 —— 当你比较它们时,它是按值记录结构添加 IEquatablelt,Tgt, 支持和 运算符记录结构提供 IEquatablelt,Tgt, 的自定义实现以避免反射的性能问题,并且它们包括记录功能,如 ToString 覆盖
记录结构可以是位置的,主构造函数隐式声明公共成员:
publicrecordstructPerson,
主构造函数的参数成为记录结构的公共自动实现属性与 record 类不同,隐式创建的属性是读 / 写的这使得将元组转换为命名类型变得更加容易将返回类型从 之类的元组更改为 Person 的命名类型可以清理你的代码并保证成员名称一致声明位置记录结构很容易并保持可变语义
如果你声明一个与主要构造函数参数同名的属性或字段,则不会合成任何自动属性并使用你的。
要创建不可变的记录结构,请将 readonly 添加到结构或将 readonly 应用于单个属性对象初始化器是可以设置只读属性的构造阶段的一部分
varperson=newPersonFirstName="Mads",LastName="Torgersen",publicreadonlyrecordstructPersonpublicstringFirstNameget,init,publicstringLastNameget,init,
在本文中了解有关记录结构的更多信息。
记录结构
03 Record 类中 ToString 上的密封修饰符
记录类也得到了改进从 C# 10 开始,ToString 方法可以包含 seal 修饰符,这会阻止编译器为任何派生记录合成 ToString 实现
在本文中的记录中了解有关 ToString 的更多信息。
有关ToString 的更多信息
04 结构和匿名类型的表达式
C# 10 支持所有结构的 with 表达式,包括记录结构,以及匿名类型:
varperson2=personwithLastName="Kristensen",
这将返回一个具有新值的新实例你可以更新任意数量的值你未设置的值将保留与初始实例相同的值
在本文中了解有关 with 的更多信息
了解有关with的更多信息
内插字符串改进
当微软在 C# 中添加内插字符串时,微软总觉得在性能和表现力方面,使用该语法可以做更多事情。
01 内插字符串处理程序
今天,编译器将内插字符串转换为对 string.Format 的调用这会导致很多分配 —— 参数的装箱,参数数组的分配,当然还有结果字符串本身此外,它在实际插值的含义上没有任何回旋余地
在 C# 10 中,微软添加了一个库模式,允许 API接管对内插字符串参数表达式的处理。例如,考虑 StringBuilder.Append:
varsb=newStringBuilder,sb.Append($"Helloargs(0),howareyou。"),
到目前为止,这将使用新分配和计算的字符串调用 Append 重载,将其附加到 StringBuilder 的一个块中但是,Append 现在有一个新的重载 Append (refStringBuilder.AppendInterpolatedStringHandler handler),当使用内插字符串作为参数时,它优先于字符串重载
有时你只想在特定条件下完成构建字符串的工作。一个例子是 Debug.Assert:
Debug.Assert"),
在大多数情况下,条件为真,第二个参数未使用但是,每次调用都会计算所有参数,从而不必要地减慢执行速度Debug.Assert 现在有一个带有自定义插值字符串构建器的重载,它确保第二个参数甚至不被评估,除非条件为假
最后,这是一个在给定调用中实际更改字符串插值行为的示例:String.Create 允许你指定 IFormatProvider 用于格式化插值字符串参数本身的洞中的表达式:
String.Create,
你可以在本文和有关创建自定义处理程序的本教程中了解有关内插字符串处理程序的更多信息。
创建自定义处理程序
内插字符串处理程序的更多信息
02 常量内插字符串
如果内插字符串的所有洞都是常量字符串,那么生成的字符串现在也是常量。这使你可以在更多地方使用字符串插值语法,例如属性:
instead"))
请注意,必须用常量字符串填充洞其他类型,如数字或日期值,不能使用,因为它们对文化敏感,并且不能在编译时计算
其他改进
C# 10 对整个语言进行了许多较小的改进其中一些只是使 C# 以你期望的方式工作
在解构中混合声明和变量
在 C# 10 之前,解构要求所有变量都是新的,或者所有变量都必须事先声明。在 C# 10 中,你可以混合:
intx2,inty2,=(0,1),//WorksinC#9(varx,vary)=(0,1),//WorksinC#9(x2,vary3)=(0,1),//WorksinC#10onwards
在有关解构的文章中了解更多信息。
改进的明确分配
如果你使用尚未明确分配的值,C# 会产生错误C# 10 可以更好地理解你的代码并且产生更少的虚假错误这些相同的改进还意味着你将看到更少的针对空引用的虚假错误和警告
在 C# 10 中的新增功能文章中了解有关 C# 确定赋值的更多信息。考虑醋酸行业开工持续高位,以及下游终端淡季影响,醋酸市场商谈逐渐有所偏弱。
C# 10中的新增功能文章
扩展的属性模式
C# 10 添加了扩展属性模式,以便更轻松地访问模式中的嵌套属性值。。例如,如果微软在上面的 Person 记录中添加一个地址,微软可以通过以下两种方式进行模式匹配:
objectobj=newPersonFirstName="Kathleen",LastName="Dollard",Address=newAddressCity="Seattle",ifConsole.WriteLine("Seattle"),if(objisPersonAddress.City:"Seattle")//ExtendedpropertypatternConsole.WriteLine("Seattle"),
扩展属性模式简化了代码并使其更易于阅读,尤其是在匹配多个属性时。
在模式匹配文章中了解有关扩展属性模式的更多信息。
模式匹配文章
调用者表达式属性
CallerArgumentExpressionAttribute 提供有关方法调用上下文的信息与其他 CompilerServices 属性一样,此属性应用于可选参数
voidCheckExpression)string。但是利润仍然处于同比偏高水平,而从库存反馈看,我们不认为醋酸出现了特别明显的过剩,未来醋酸对甲醇需求的拉动仍然是有保障的。message=null)Console.WriteLine($"Condition:message"),
传递给 CallerArgumentExpression 的参数名称是不同参数的名称作为参数传递给该参数的表达式将包含在字符串中
vara=6,varb=true,CheckExpression,CheckExpression(b),CheckExpression(agt,5),//Output://Condition:true//Condition:b//Condition:agt,5
ArgumentNullException.ThrowIfNull 是如何使用此属性的一个很好的示例。它通过默认提供的值来避免必须传入参数名称:
voidMyMethodArgumentNullException.ThrowIfNull(value),
了解有关 CallerArgumentExpressionAttribute 的更多信息
。声明:本网转发此文章,旨在为读者提供更多信息资讯,所涉内容不构成投资、消费建议。文章事实如有疑问,请与有关方核实,文章观点非本网观点,仅供读者参考。