本文转载自微信公众号《老王加》,老王加作者老王。转载本文请联系老王Plus公众号。最近在研究Dotnet6.0&C#10,一个字——爽!现在,我将与您分享一些新功能。1、编程语言方面,编程语言升级到C#10,新东西很多。1.请参阅所需属性的简单示例:publicclassUser{publicstringname{get;set;}publicDateTimedateOfBirth{get;set;}}假设我们希望输入dateOfBirth字段。在C#9.0之前,我们其实也没有更好的办法。例如:varmyUser=newUser(){name="WangPlus",}这样写,编译器不会给出任何提醒或警告。因此,我们需要在各个使用的地方加入字段赋值检查。在最新的语言中,为这样的需求添加了一个required属性。看代码:publicclassUser{publicstringname{get;set;}publicrequiredDateTimedateOfBirth{get;set;}}这时候在实例化User时没有给dateOfBirth赋值,编译器会直接抛出异常。在写这篇文章的时候,我刚刚发现这个特性在最新的预览版中被移除了,似乎微软想把它放在C#11中。现在论坛上一片哗然。让我们静待结果。2.属性的字段大多数情况下,我们定义一个类是这样的:提前实例化,或者在实例化时需要进行一些处理。例如,在上面的类中,我们只想要dateOfBirth的日期部分,我们将这样创建类:;}}}当然,习惯了也没关系。但是,我们知道私有属性_dateOfBirth其实是一个中间数据,对外没有用,但是会占用实例资源。现在,有一个称为字段的中间属性。代码会变成这样:publicclassUser{publicstringname{get;set;}publicDateTimedateOfBirth{get;set=>field=value.Date;}}嗯,可读性高很多。3、匿名对象匿名对象的出现给我们带来了很多方便。在一个类中,可以将不需要对外输出的结构化数据做成匿名对象,而不需要预先定义一个数据对象。看代码:varmyUser=new{name="WangPlus",gender="Male"};真的很方便。但是,也有不便之处,就是匿名对象的传递。比如我们要再创建一个对象myUser1,属性还是一样,如果只需要改变几个属性的值怎么办?以前没办法,只能重写一个:varmyUser1=new{name="WangPlus1",gender="Male"};/**or**/varmyUser1=new{name="WangPlus1",gender=我的用户性别};可以想象,如果这个匿名对象有很多字段,那就麻烦了。现在有了with,这件事就简单了:varmyUser1=myUserwith{name="WangPlus1"};注意这种写法不是改变myUser中的属性,而是生成一个新的实例并将myUser属性和值的所有细节传递给新实例myUser1,然后将一些属性的值改为新的价值观。4.非空参数检查当我们写一个方法时,成熟的程序员都会检查非空参数:publicstringFormatName(stringname){if(string.isNullOrEmpty(name))return"ERROR";/**...**/}publicstringFormatUser(Useruser){if(user==null)return"ERROR";/**...**/}方法正确,但是很麻烦。这是例行公事,但必须保留下来。现在,有个神参数:!,没错,就是感叹号。是这样写的:publicstringFormatName(stringname!){/**...**/}publicstringFormatUser(Useruser!){/**...**/}加上!后程序会自动检查参数execution非空状态,如果有null,会抛出ArgumentNullExceptions。5.全局使用这是最酷的功能之一。以前我们写代码的时候,每个文件前面都有无数的using,很多using都是重复的。现在,C#10提供了一个全局关键字。从此using变成了:globalusingSystem;globalusingSystem.Collections.Generic;globalusingSystem.Threading.Tasks;系统会识别globalusing背后的内容会应用到整个项目中。所以在其他文件中,如果需要使用,不需要写对应的using,直接写代码即可。因此,所有的全局using都可以放在一个单独的文件中,在其他文件中,不需要做using引用。同时,如果已经有一个全局的using,而你在文件中写了同一个库的using,系统会抛出一个警告。6.File-levelnamespacenamespace这个特性好像并没有省事多少。然而,这也是一种变化。我们以前做代码的时候是这样的:namespaceMyNamespace{publicclassUser{publicvoidUser(){//...方法实现}}}对外调用的时候,只要这样写:varobj=newMyNamespace.User();/**or**/usingMyNamespace;varobj=newUser();现在,把命令空间的定义改成:namespaceMyNamespace;publicclassUser{publicvoidUser(){//...Methodimplementation}}这样写,清爽多了一点,缩进层次少了一个。当然,调用还是一样的。二、API方面API方面比较多。在社区中,新的API不断涌现。我只挑一些我觉得有用的。1、文件的非流式读写文件的流式读写往往会涉及到中间流,既浪费资源又写起来麻烦。现在可以直接使用低级IO读写了。方法被添加到File类。varhandler=File.OpenHandle("abc.txt");varlength=RandomAccess.GetLength(handler);2、强随机数我们知道前面的随机数Random类是弱随机数,它来自于算法,无法实现真正??的随机。生成的随机数序列取决于种子,相同的种子产生相同的随机数序列。因此,为了得到不同的随机数序列,我们通常这样写:varrand=newRandom((int)DateTime.Now.Ticks);当然,这通常就足够了。但总有一些特殊的需要真正的随机数,即强随机数。在DotnetCore6.0中,提供了一个RandomNumberGenerator类。byte[]bytes=RandomNumberGenerator.GetBytes(200);intrandomInt=RandomNumberGenerator.GetInt32(0,10000);另外需要注意的是,这个类不在System空间,而是在System.Security.Cryptography中。3.多任务异步Parallel.ForEachAsync在多任务中,曾经只有一种Parallel.ForEach方法用于同步执行。这次终于加入了异步方法Parallel.ForEachAsync,足以看出微软深耕异步的决心。写法还是我们熟悉的,这个切换也很简单:varurls=new[]{"https://test1.com","https://test2.com"};varclient=newHttpClient();awaitParallel.ForEachAsync(urls,async(url,token)=>{HttpResponseMessageresponse=awaitclient.GetAsync(url);});4.Scheduledasynchronousabort这也是一个不错的API。以前在等待一个异步进程时,如果进程长时间不能结束,我们只能通过CancellationToken来结束。现在,我们有另一种方式,我们可以通过Timeout的形式设置一个时间来结束这个异步过程。TasksomeTask=SomeLongRunningTaskAsync();awaitsomeTask.WaitAsync(TimeSpan.FromSeconds(10));如果你写过CancellationToken来结束异步代码,你就知道这个WaitAsync有多好。5.ThrowIfNull其实和判断参数是否为空是一样的。当我们添加!到参数数据末尾做空判断,实际执行语句:publicstringFormatUser(Useruser){ArgumentNullException.ThrowIfNull(user);}如果对象为空,则抛出ArgumentNullException。6、使用直接内存以前使用不安全内存malloc时,空间是在堆上分配的。现在有直接内存分配空间的方法:usingSystem.Runtime.InteropServices;unsafe{byte*buffer=(byte*)NativeMemory.Alloc(128);NativeMemory.Free(buffer);}对于嵌入式开发,有福了。此外,对于非托管内容,通常需要大小对齐。所谓对齐,就是分配空间的大小需要是2的整数索引。平时大家直接计算写就可以了,现在有更灵活的方式:usingSystem.Numerics;uintbufferSize=211;if(!BitOperations.IsPow2(bufferSize)){bufferSize=BitOperations.RoundUpToPowerOf2(bufferSize);}给一个空格,如果空格的大小不是2的整数指数,就找一个比这个数大的2的整数指数。又省事了。7.新的定时器很多文章都把这个定时器称为ModernTimer,足以看出它有多优秀。好事?这是一个异步定时器。之前的定时器Timer,无论是在System.Timers、System.Threading,还是System.Windows.Forms下,都是一个同步定时器,需要使用Tick事件绑定来实现回调。这种方式使得定时器非常依赖于上级对象的生命周期,以至于在UI编程中需要Invoke来引入回调响应。现在这个就简单多了:vartimer=newPeriodicTimer(TimeSpan.FromSeconds(1));while(awaittimer.WaitForNextTickAsync()){/**...**/}这种写法看着舒服。写了很多,其实只是冰山一角。
