.Net Core 教程 Part4-(1)(2)(3)(4)(5) C# 新语法

前言

C#8.0 C#9.0 和 C#10.0中增加了很多新语法,这里讲解常用、重点的新语法。

部分语法只有VS2022及以上版本才支持,从本节开始使用VS2022+ .net6。

Part4-1 顶级语句(C#9.0)

1.直接在C#文件中直接编写入口方法的代码,不用类,不用Main。经典写法仍然支持。反编译一下了解真相。

2.同一个项目中只能有一个文件具有顶级语句。

3.顶级语句中可以直接使用await语法,也可以声明函数。

.Net Core 教程 Part4-(1)(2)(3)(4)(5) C# 新语法
控制台程序默认没有Main

原来的Main语法也支持,还可以继续使用。对于代码片段比较简单的情况,推荐缺省Main入口函数,直接写内容。

.Net Core 教程 Part4-(1)(2)(3)(4)(5) C# 新语法
以前使用Main入口函数的情况
.Net Core 教程 Part4-(1)(2)(3)(4)(5) C# 新语法
顶级语句直接使用await

这其实是语法糖,其实是编译器帮助我们把Main函数给写了。

Part4-2 全局USING指令(C#10.0)

1.将global修饰符添加到using前,这个命名空间就应用到整个项目,不用重复using。(只要有一个地方用了global,全局都不需要再using这个命名空间了。)

2.通常创建一个专门用来编写全局using代码的C#文件。

3.如果csproj中启用了<ImplicitUing>enbale</ImplicitUsing>, 编译器会自动隐式增加对于System、 System.Linq等常用命名空间的引入,不同各类型项目引入的命名空间也不一样。

.Net Core 教程 Part4-(1)(2)(3)(4)(5) C# 新语法
在某个文件中统一global引用
.Net Core 教程 Part4-(1)(2)(3)(4)(5) C# 新语法
默认开启ImplicitUsings 隐式增加引用

Part4-3 USING声明(C# 8.0)

USING资源管理的问题

复习: 实现了IDsposible接口的对象可以用using进行管理。

问题: 如果有一段代码中有很多非托管资源需要释放的话,代码中就会存在着多个嵌套的using语句。

using (var conn = new SqlConnection("...")
{
    conn.Open();
    using (var cmd = conn.CreateCommand())
    {
        cmd.CommandText = "select * from T_Articles";
        using(var reader = cmd.ExecuteReader())
        {
            while(reader.Read())
            {
            }
        }
    }
}

USING 声明

在实现了Idispsoable/AAsyncDisposable接口的类型的变量声明前加上using,当代码执行离开变量的作用域时,对象就会被释放。

using SqlConnection conn = new SqlConnection("Data Source=.;Initial Catalog=db1;Integrated Security=True");
conn.Open();
using SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = "select * from T_Articles";
using SqlDataReader reader = cmd.ExecuteReader();
While(reader.Read())
{
    string title = reader.GetString(reader.GetOrdinal("Title"));
    Console.WriteLine(title);
}

下面我们举一个例子来看看。在while中使用using,离开 {} 就可以自动销毁。

.Net Core 教程 Part4-(1)(2)(3)(4)(5) C# 新语法
MyFile继承IDisposable接口
.Net Core 教程 Part4-(1)(2)(3)(4)(5) C# 新语法
在While中使用using

但是,使用过程中也要注意到一些坑。下面是一个例子,先写入文件,再立即读出文件内容,会发现文件被占用,还在继续写,所以报错了。

  • 解决方案一:用传统的using大括号,明确声明生命周期。
  • 解决方案二:直接用大括号阔起来,看起来怪怪的,但也可以这么用。
.Net Core 教程 Part4-(1)(2)(3)(4)(5) C# 新语法
using的使用小坑
.Net Core 教程 Part4-(1)(2)(3)(4)(5) C# 新语法
解决方案

Part4-4 文件范围的命名空间声明(C# 10.0)

1.在之前版本的C#中,类型必须定义在namespace中。

2.不用写大括号,直接写在class上面;Teacher这个类还是属于TMS.Admin这个命名空间。

namespace TMS.Admin; 
class Teacher
{
    public int Id(get;set;)
    public int Name(get;set;)
}

Part4-5 可空引用类型 (C# 8.0)

1.复习: C#数据类型分为值类型和引用类型两种,值类型的变量不可以为空,而引用类型变量可以为空。

2.问题: 如果不注意检查引用类型变量是否可控,就有可能造成程序中出现 NullReferenceException 异常。

  • csproj中 <Nullable>enable</Nullable>启用可空引用类型检查
  • 在引用类型后添加 “?” 修符来声明这个类型是可空的,对于没有添加 “?” 修饰符的引用类型的变量,如果编译器发现存在为这个变量赋值null的可能性的时候,编译器会给出警告信息
.Net Core 教程 Part4-(1)(2)(3)(4)(5) C# 新语法
默认设置的是可空警告enable

有警告的声明

在定义一个类的属性的时候,如果没有使用 string? Name={get;set;}这个?方式的方式来声明可空类型的话,那将来就有可能在实例化的时候产生空引用的错误,所以这里会有告警。

.Net Core 教程 Part4-(1)(2)(3)(4)(5) C# 新语法
null警告
.Net Core 教程 Part4-(1)(2)(3)(4)(5) C# 新语法
初始化函数避免非空
.Net Core 教程 Part4-(1)(2)(3)(4)(5) C# 新语法
编译器智能判断与警告

Part4-6 记录(Recored)类型(C# 9/0)

1.C#中的==运算默认是判断两个变量指向的是否是同一个对象,即使两个对象内容完全一样,也不相等。可以通过重写Equals方法,重写运算符==等来解决这个问题,不过需要开发人员编写非常多的额外代码。

2.在C#9.0中增加了记录(record)类型的语法,编译器为我们自动生成Equals、GetHashcode等方法。

public record Person(string FirstName, string LastName);
.Net Core 教程 Part4-(1)(2)(3)(4)(5) C# 新语法
.Net Core 教程 Part4-(1)(2)(3)(4)(5) C# 新语法

对于很多实体类和对象,如果需要ToString或者比较两个对象是否相等,编译器可以自动来做这个事情。看如下例子:

.Net Core 教程 Part4-(1)(2)(3)(4)(5) C# 新语法
.Net Core 教程 Part4-(1)(2)(3)(4)(5) C# 新语法

探究record原理

1.编译器会根据Person类型中的属性定义,自动为Person类型生成包含全部属性的构造方法。注意,默认情况下,编译器会生成一个包含所有属性的构造方法。因此我们编写new Person(), new Person(“Zheng”)这两种写法都是不可以的。也会生成ToString方法和Equals等方法。

2.通过反编译看背后原理。避免反编译器的优化,需要把反编译器生成的代码改成C#8.0语法。结论: record就是普通的一个类。

3. Record数据类型为我们提供了为所有属性赋值的构造方法,所有属性都是只读的,而且对象可以进行值相等性比较,并且提供了可读性强的ToString()返回值。在需要编写一些不可变类并且需要进行对象值比较的对象时候,record可以帮我们把代码的编写难度大大降低。

Record深入

1.可以实现部分属性是只读的,而部分属性可以读写。

2.默认生成的构造方法的行为不能修改,我们可以为类型提供多个构造方法,然后其他构造方法通过this调用默认的构造方法。

3.也推荐使用只读属性的类型。这样的所有属性都为只读的类型叫做“不可变类型”,可以让程序逻辑简单,减少并发访问,状态管理等的麻烦。

.Net Core 教程 Part4-(1)(2)(3)(4)(5) C# 新语法
.Net Core 教程 Part4-(1)(2)(3)(4)(5) C# 新语法
.Net Core 教程 Part4-(1)(2)(3)(4)(5) C# 新语法

自定义record构造函数

internal record Student(int Id, string Name)
{
    public string? NickName { get; set; }
    public Student(int Id,string Name, string nickName):this(Id,Name)
    {
        this.NickName = nickName;
    }
}

对象的副本

1.record 也是普通类,变量的赋值是引用的传递。这是和结构体不同之处。

2.生成一个对象的副本,这个对象的其他属性值与原对象的相同,只有一个或者少数几个属性改变。麻烦的做法:

User u2 = new User(u1.UserName, "test@example", u1.Age);

3.用with关键字简化,如下。创建的也是拷贝。

User u2 = u1 with {Email="test@example"}
.Net Core 教程 Part4-(1)(2)(3)(4)(5) C# 新语法

本文版权归个人技术分享站点所有,发布者:chaoqiang,转转请注明出处:https://www.zhengchaoqiang.com/1231.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
chaoqiangchaoqiang
上一篇 2021-10-23 20:09
下一篇 2021-11-07 13:20

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

近期个人博客正在迁移中,原博客请移步此处,抱歉!