C#: 8.0 和 9.0 常用新特性

在《带你了解C#每个版本新特性》 一文中介绍了,C# 1.0 到 7.0 的不同特性,本文接着介绍在 8.0 和 9.0 中的一些常用新特性。

C# 8.0

在 dotNET Core 3.1 及以上版本中就可以使用 C# 8 的语法,下面是 C# 8 中我认为比较常用的一些新功能。

默认接口方法

接口是用来约束行为的,在 C# 8 以前,接口中只能进行方法的定义,下面的代码在 C# 8 以前是会报编译错误的:

public interface IUser
{string GetName() =>  "oec2003";
}

那么在 C# 8 中,可以正常使用上面的代码,也就是说可以对接口中的方法提供默认实现。

接口默认方法最大的好处是,当在接口中进行方法扩展时,之前的实现类可以不受影响,而在 C# 8 之前,接口中如果要添加方法,所有的实现类需要进行新增接口方法的实现,否则编译失败。

C# 中不支持多重继承,主要的原因是会导致菱形问题:

  • 类 A  是一个抽象类,定义有一个 方法 Test;

  • 类 B 和 类 C 继承自抽象类 A,并有各自的实现;

  • 类 D 同时继承类 B 和类 C;

当调用类 D 的 Test 方法时,就不知道应该使用 B 的 Test 还是 C 的 Test,这个就是菱形问题。

而接口是允许多继承的,那么当接口支持默认方法时,是否也会导致菱形问题呢?看下面代码:

public interface IA
{void Test() => Console.WriteLine("Invoke IA.Test");
}
public interface IB:IA
{void Test() => Console.WriteLine("Invoke IB.Test");
}
public interface IC:IA
{ void Test() => Console.WriteLine("Invoke IC.Test");
}
public class D : IB, IC { }static void Main(string[] args)
{D d = new D();d.Test();
}

上面的代码是无法通过编译的,因为接口的默认方法不能被继承,所以类 D 中没有 Test 方法可以调用,如下图:

所以,必须通过接口类型来进行相关方法的调用:

static void Main(string[] args)
{IA d1 = new D();IB d2 = new D();IC d3 = new D();d1.Test();  // Invoke IA.Testd2.Test();  // Invoke IB.Testd3.Test();  // Invoke IC.Test
}

也正是因为必须通过接口类型来进行调用,所以也就不存在菱形问题。而当具体的类中有对接口方法实现的时候,就会调用类上实现的方法:

public interface IA
{void Test() => Console.WriteLine("Invoke IA.Test");
}
public interface IB:IA
{void Test() => Console.WriteLine("Invoke IB.Test");
}
public interface IC:IA
{ void Test() => Console.WriteLine("Invoke IC.Test");
}
public class D : IB, IC 
{public void Test() => Console.WriteLine("Invoke D.Test");
}
static void Main(string[] args)
{IA d1 = new D();IB d2 = new D();IC d3 = new D();d1.Test();  // Invoke D.Testd2.Test();  // Invoke D.Testd3.Test();  // Invoke D.Test
}

类可能同时继承类和接口,这时会优先调用类中的方法:

public class A
{public void Test() => Console.WriteLine("Invoke A.Test");
}
public interface IA
{void Test() => Console.WriteLine("Invoke IA.Test");
}public class D : A, IA { }
static void Main(string[] args)
{D d = new D();IA d1 = new D();d.Test();  // Invoke A.Testd1.Test();  // Invoke A.Test
}

关于默认接口方法,总结如下:

  • 默认接口方法可以让我们在往底层接口中扩展方法的时候变得比较平滑;

  • 默认方法,会优先调用类中的实现,如果类中没有实现,才会去调用接口中的默认方法;

  • 默认方法不能够被继承,当类中没有自己实现的时候是不能从类上直接调用的。

using 变量声明

我们都知道 using 关键字可以导入命名空间,也能定义别名,还能定义一个范围,在范围结束时销毁对象,在 C# 8.0 中的 using 变量声明可以让代码看起来更优雅。

在没有 using 变量声明的时候,我们是这样使用的:

static void Main(string[] args)
{var connString = "Host=221.234.36.41;Username=gpadmin;Password=123456;Database=postgres;Port=54320";using (var conn = new NpgsqlConnection(connString)){conn.Open();using (var cmd = new NpgsqlCommand("select * from user_test", conn)){using (var reader = cmd.ExecuteReader()){while (reader.Read())Console.WriteLine(reader["user_name"]);}}}Console.ReadKey();
}

当调用层级比较多时,会出现 using 的嵌套,对影响代码的可读性,当然,当两个 using 语句中间没有其他代码时,可以这样来优化:

static void Main(string[] args)
{var connString = "Host=221.234.36.41;Username=gpadmin;Password=123456;Database=postgres;Port=54320";using (var conn = new NpgsqlConnection(connString)){conn.Open();using (var cmd = new NpgsqlCommand("select * from user_test", conn))using (var reader = cmd.ExecuteReader())while (reader.Read())Console.WriteLine(reader["user_name"]);}Console.ReadKey();
}

使用 using 变量声明后的代码如下:

static void Main(string[] args)
{var connString = "Host=221.234.36.41;Username=gpadmin;Password=123456;Database=postgres;Port=54320";using var conn = new NpgsqlConnection(connString);conn.Open();using var cmd = new NpgsqlCommand("select * from user_test", conn);using var reader = cmd.ExecuteReader();while (reader.Read())Console.WriteLine(reader["user_name"]);Console.ReadKey();
}

Null 合并赋值

这是一个很有用的语法糖,在 C# 中如果调用一个为 Null 的引用类型上的方法,会出现经典的错误:”未将对应引用到对象的实例“,所以我们在返回引用类型时,需要做些判断:

static void Main(string[] args)
{List<string> list = GetUserNames();if(list==null){list = new List<string>();}Console.WriteLine(list.Count);
}
public static List<string> GetUserNames()
{return null;
}

在 C# 8 中可以使用 ??= 操作符更简单地实现:

static void Main(string[] args)
{List<string> list = GetUserNames();list ??= new List<string>();Console.WriteLine(list.Count);
}

当 list 为 null 时,会将右边的值分配给 list 。

C# 9.0

在 .NET 5 中可以使用 C# 9 ,下面是  C# 9 中几个常用的新特性。

init

init 是属性的一种修饰符,可以设置属性为只读,但在初始化的时候却可以指定值:

public class UserInfo
{public string Name { get; init; }
}
UserInfo user = new UserInfo { Name = "oec2003" };
//当 user 初始化完了之后就不能再改变 Name 的值
user.Name = "oec2004";

上面代码中给 Name 属性赋值会出现编译错误:

record

在 C# 9 中新增了 record 修饰符,record 是一种引用类型的修饰符,使用 record 修饰的类型是一种特别的 class,一种不可变的引用类型。

我们创建一个名为 UserInfo 的 class ,不同的实例中即便属性值完全相同,这两个实例也是不相等的,看下面代码:

public class UserInfo
{public string Name { get; set; }
}
static void Main(string[] args)
{UserInfo user1 = new UserInfo { Name = "oec2003" };UserInfo user2 = new UserInfo { Name = "oec2003" };Console.WriteLine(user1== user2); //False
}

如果使用 record ,将会看到不一样的结果,因为 record 中重写了 ==、Equals 等 ,是按照属性值的方式来进行比较的:

public record UserInfo
{public string Name { get; set; }
}
static void Main(string[] args)
{UserInfo user1 = new UserInfo { Name = "oec2003" };UserInfo user2 = new UserInfo { Name = "oec2003" };Console.WriteLine(user1== user2); //True
}

在 class 中我们经常将一个对象的实例赋值给另一个值,对赋值后的对象实例进行属性值的改变会影响到原对象实例:

public class UserInfo
{public string Name { get; set; }
}
static void Main(string[] args)
{UserInfo user = new UserInfo { Name = "oec2003" };UserInfo user1 = user;user1.Name = "oec2004";Console.WriteLine(user.Name); // oec2004
}

如果想要不影响原对象实例,就需要使用到深拷贝,在 record 中,可以使用 with 语法简单地达到目的:

public record UserInfo
{public string Name { get; set; }
}
static void Main(string[] args)
{UserInfo user = new UserInfo { Name = "oec2003" };UserInfo user1 = user with { Name="eoc2004"};Console.WriteLine(user.Name); // oec2003Console.WriteLine(user1.Name); // oec2004
}

模式匹配增强

模式匹配中我觉得最有用的就是对 Null 类型的判断,在 9.0 中支持这样的写法了:

public static string GetUserName(UserInfo user)
{if(user is not null){return user.Name;}return string.Empty;
}

顶级语句

这个不知道有啥用?但挺好玩的,创建一个控制台程序,将 Program.cs 中的内容替换为下面这一行,程序也能正常运行:

System.Console.WriteLine("Hello World!");

除此之外,在 C# 8.0 和 9.0 中还有一些其他的新功能,我目前没有用到或者我觉得不太常用,就没有写在本文中了。

希望本文对您有所帮助。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/303089.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

选股公式 成功率测试 软件,通达信股票软件选股公式,99%的成功率,你还等什么?...

工具&#xff1a;通达信金融终端使用方法&#xff1a;1、通达信软件界面2、点击“功能”“公式系统”“公式管理器”“条件选股公式”“其他类型”&#xff0c;点击新建3、使用选股公式&#xff0c;点击ctrlt&#xff0c;弹出条件选股窗口4、选中公式&#xff0c;点击加入条件&…

小米 华为都要造车?.NET高薪潮来了!(附招聘链接)

近期网络沸沸扬扬的小米、华为、苹果 都要造车&#xff0c;其实百度、阿里和腾讯 也都入局了。互联网大佬们纷纷开始造车&#xff0c;跟我们小小程序员有关系吗&#xff1f;有&#xff01;高薪机会来了&#xff0c;尤其是WPF&#xff01;且听下文分解。物联网终极形态“互联网”…

聚类算法学习指南(二)

http://hi.baidu.com/catfool/blog/item/c06bec3931a0efcad4622524.html 聚类算法学习指南&#xff08;二&#xff09;2009-05-06 20:49下图图 3-1 聚类方法的分类示意图3.1 传统聚类算法3.1.1 层次方法层次法对给定的数据对象集合进行层次似的分解。按层次分解的形成方式&…

.NetCore跨域

描述浏览器安全策略上的安全限制可以有效阻止Ajax向另外的一个域server发起请求&#xff0c;这就是著名的同源策略&#xff0c;如何突破这种限制&#xff0c;可以使用CORS。public static void AddCommon(this IServiceCollection services){services.AddCors(options >{opt…

山东大学计算机非全上课,山东大学非全日制研究生上课方式

【导读】山东大学非全日制研究生上课方式。如今非全日制研究生考试已经与全日制一样&#xff0c;连考试内容和时间也都是一样。许多想要报考山东大学非全日制研究生的人员开始担心&#xff0c;非全日制研究生和全日制研究生的考试样了&#xff0c;那么上课方式会不会也是一样的…

【转载】可复用的FS

有很多朋友要求给出一个应用ESFramework的Demo&#xff0c;在前面介绍ESFramework支持的4层架构中&#xff08;回顾&#xff09;&#xff0c;有AS、FS、IRAS&#xff0c;其中FS的功能需求最简单&#xff0c;所以本文就给出一个FS实现示例。 FS主要用于管理功能插件&…

Git、GitHub、GitLab三者之间的联系以及区别

在讲区别以及联系之前先简要的介绍一下&#xff0c;这三者都是什么(本篇文章适合刚入门的新手&#xff0c;大佬请出门左转)1.什么是 Git?Git 是一个版本控制系统。版本控制是一种用于记录一个或多个文件内容变化&#xff0c;方便我们查阅特定版本修订情况的系统。以前在没有使…

zen服务器芯片,服务器版Zen处理器简直大杀器:32核64线程,8通道DDR4内存

拼 命 加 载 中 ...在这次的E3展会上&#xff0c;AMD除了公布RX 470/460显卡之外&#xff0c;还跟雷蛇发了一款支持VR的背包PC&#xff0c;使用了RX480显卡&#xff0c;处理器则是AMD的Zen&#xff0c;8核16线程&#xff0c;这还只是桌面版的&#xff0c;要知道Zen处理器最重要…

【赠书活动】优雅免费领书指南

快扶朕起来&#xff0c;我还要送书&#xff01;在上一期的赠书活动上结束后。超模君就吩咐小树包装好书籍&#xff0c;寄给获奖的模友们&#xff01;很多模友收到书籍都表示很不错&#xff0c;期望能再来一次赠书活动。这不&#xff0c;朕又想&#xff0c;超模君又想送书了&…

利用11行Python代码,盗取了室友的U盘,内容十分刺激!

作者&#xff1a;Python新世界 來源&#xff1a;简书https://www.jianshu.com/p/3d7b2d9fe679序言那个猥琐的家伙整天把个U盘藏着当宝&#xff0c;都睡觉了就拿出来插到电脑上。我决定想个办法看他U盘里都藏了什么&#xff0c;直接去抢U盘是不可能的&#xff0c;骗也是…

鸡肋还是革新——Blazor进WinForm

winform是一老技术&#xff0c;感觉都有点掉牙了&#xff08;我近20年前就是从winform开始接触.net的&#xff09;&#xff1b;blazor&#xff0c;是微软技术圈里的新宠&#xff0c;正在被悉心照顾。当这一老一少的技术碰撞后&#xff0c;会有什么火花&#xff1f;.net v6.0.0-…

一个女程序员征男友的需求说明书

前 言常听人说程序员的生活枯燥为人刻板&#xff0c;其实这是你不懂程序员。代码之外&#xff0c;生活之中&#xff0c;这些高智商人的幽默风趣&#xff0c;才华的展现往往能在「论坛」这个神奇的领域一窥究竟。&#xff08;悲哀&#xff0c;因为给他们展现Coder之外才华的地方…

查看服务器物理内存大小,如何看服务器的物理内存大小

如何看服务器的物理内存大小 内容精选换一换JVM在执行Java程序时会把它所管理的内存划分为若干个不同的运行时数据区域&#xff0c;主要包括&#xff1a;程序计数器、方法区、虚拟机栈、本地方法栈和堆&#xff1a;程序计数器可以看作时当前线程所执行的字节码的行号指示器。方…

通过Dapr实现一个简单的基于.net的微服务电商系统(四)——一步一步教你如何撸Dapr之订阅发布...

之前的章节我们介绍了如何通过dapr发起一个服务调用&#xff0c;相信看过前几章的小伙伴已经对dapr有一个基本的了解了&#xff0c;今天我们来聊一聊dapr的另外一个功能——订阅发布目录&#xff1a;一、通过Dapr实现一个简单的基于.net的微服务电商系统二、通过Dapr实现一个简…

值得收藏的Python小技巧:这17个骚操作你都OK吗?

导读&#xff1a;Python 是一门非常优美的语言&#xff0c;其简洁易用令人不得不感概人生苦短。在本文中&#xff0c;作者 Gautham Santhosh 带我们回顾了 17 个非常有用的 Python 技巧&#xff0c;例如查找、分割和合并列表等&#xff0c;这 17 个技巧都非常简单&#xff0c;但…

等待十年,史上第一个 64 位版 Visual Studio 将于今夏公开首个预览版!

昨日&#xff0c;微软在其开发者博客宣布了一则重磅消息——Visual Studio 2022 首个预览版将于今年夏季发布 &#xff0c;并且终于成为万众期待的 64 位版&#xff01;要知道&#xff0c;早在 2011 年就有用户在 Visual Studio 的反馈建议网站提出&#xff0c;希望微软开发 64…

普通人和程序员看到的树

IT程序猿 微博网友评论&#xff1a;小苏打_sct&#xff1a;我家门前有两棵树&#xff0c;一棵是二叉树&#xff0c;另一棵也是二叉树( _ゝ&#xff40;)Christible_L&#xff1a;这不是多叉树吗 北辰找不着北&#xff1a;程序员都是从根看起的~ 洱海不语&#xff1a;先先总体到…

华为高性能服务器刀箱,云平台服务器刀箱

云平台服务器刀箱 内容精选换一换将外部镜像文件注册成云平台的私有镜像后&#xff0c;您可以使用该镜像创建新的云服务器&#xff0c;或对已有云服务器的系统进行重装和更换。本节介绍使用镜像创建云服务器的操作。您可以按照通过镜像创建云服务器中的操作指导创建弹性云服务器…

Magicodes.IE.AspNetCore之一行代码多格式导出

主要步骤1.安装包Install-Package Magicodes.IE.AspNetCore2.开始配置在Startup.cs的Configure()方法中,在UseRouting()中间件之后,注册如下中间件public void Configure(IApplicationBuilder app) {app.UseRouting();app.UseMagiCodesIE();app.UseEndpoints(endpoints >{en…

想不到吧,这些都能用R!

R作为一种统计分析软件&#xff0c;广泛应用于生物、医学、电商、新闻等数据相关行业&#xff0c;是目前主流数据应用软件之一。那么&#xff0c;R到底有哪些特别之处呢?实际上&#xff0c;R是统计领域广泛使用的S语言的一个分支&#xff0c;两者在程序语法上几乎一样&#xf…