C# 9.0 特性全面总结

作者:张志敏 时间:2023-07-21 16:28:19 

目录
  • 顶级语句

  • 弃元参数

  • 仅初始化设置器 (Init only setters)

  • 记录类型 (Record)

  • 模式匹配增强

    • Type patterns 类型匹配,判断一个变量的类型

    • Relational patterns 关系匹配

    • Conjunctive and patterns 逻辑与匹配

    • Disjunctive or patterns 逻辑或匹配

    • Negated not patterns 逻辑非匹配

    • Parenthesized patterns 带括号的优先级匹配

  • 新的初始化表达式

    • 目标类型条件表达式

      • GetEnumerator 扩展

        • 在本地函数上添加标记

          • 分部方法扩展

            • 静态 lambda 表达式

              • 模块初始化代码

                • 协变返回类型

                  • 原生整数类型

                    • 跳过本地初始化 (SkipLocalInit)

                      • 函数指针

                        顶级语句

                        顶级语句可以删除程序中不必要的代码, 以最简单的 Hello, world! 为例:


                        using System;

                        namespace HelloWorld {

                        class Program {

                        static void Main(string[] args) {
                          Console.WriteLine("Hello World!");
                         }
                        }
                        }

                        如果使用顶级语句的话, 可以简化为:


                        using System;

                        Console.WriteLine("Hello World!");

                        如果不使用 using , 还可以更加简化:


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

                        顶级语句在很多命令行程序、小工具程序中会非常有用, 对应用程序的作用域或者复杂程度没有任何限制。

                        注意, 一个程序中, 只能有一个文件使用顶级语句, 并且顶级语句必须位于命名空间或类型定义之前!

                        弃元参数

                        在 lambda 表达式或者匿名函数中如果要忽略某个参数, 可以用 _ 代替。


                        var button = new Button("Click Me!");

                        button.Click += (_, e) => { /* other code goes here. */ };

                        仅初始化设置器 (Init only setters)

                        创建只能通过对象初始化进行赋值的属性。


                        public class InitDemo {
                        public string Start { get; init; }
                        public string Stop { get; init; }
                        }

                        // initDemo.Start = "Now"; // Error
                        // initDemo.End = "Tomorrow"; // Error

                        var initDemo = new InitDemo {
                        Start = "Now",
                        Stop = "Tomorrow"
                        };

                        记录类型 (Record)

                        记录类型, 是一种引用类型, 默认是不可变的。 记录类型的相等判断可以通过引用或者结构进行判断的。


                        // 默认不可变的记录类型
                        public record Person(string Name, int Age);
                        // 可变记录类型
                        public record MutablePerson(string Name, int Age) {
                        public string Name { get; set; } = Name;
                        public int Age { get; set; } = Age;
                        }

                        var person1 = new Person("Zhimin Zhang", 40);
                        var person2 = new Person("Zhimin Zhang", 40);

                        Console.WriteLine(person1 == person2); // True 结构相同
                        Console.WriteLine(person1.Equals(person2)); // True 结构相同
                        Console.WriteLine(ReferenceEquals(person1, person2)); // False, 引用不同

                        // 改变默认的记录! --> 创建一个新的记录。
                        var person3 = person1 with { Age = 43 };
                        Console.WriteLine(person3 == person1); // False 结构不同

                        // 解构 (Destruct) 一个记录, 将记录的属性提取为本地变量
                        var (name, age) = person3;

                        var person4 = new MutablePerson("Zhimin zhang", 40);
                        person4.Age = 43;

                        var person5 = new Citizen("Zhimin Zhang", 40, "China");
                        Console.WriteLine(person1 == person5);

                        // 记录类型也可以被继承
                        public record Citizen(string Name, int Age, string Country) : Person(Name, Age);
                        var citizen = new Citizen("Zhimin Zhang", 40, "China");
                        Console.WriteLine(person1 == citizen); // False 类型不同;
                        • 优点:记录类型是轻量级的不可变类型,可以减少大量的代码, 可以按照结构和引用进行比较;

                        • 缺点:需要实例化大量的对象;

                        如果要更加深入的学习记录类型, 请查看微软的官方文档 exploration of records 。

                        模式匹配增强

                        C# 9 包含了一些新的模式匹配增强:

                        Type patterns 类型匹配,判断一个变量的类型


                        object obj = new int();
                        var type = obj switch {
                        string => "string",
                        int => "int",
                        _ => "obj"
                        };
                        Console.WriteLine(type); // int

                        Relational patterns 关系匹配


                        // Relational patterns
                        var person1 = new Person("Zhimin Zhang", 40);
                        var inRange = person1 switch {
                        (_, < 18) => "less than 18",
                        (_, > 18) => "greater than 18",
                        (_, 18) => "18 years old!"
                        };
                        Console.WriteLine(inRange); // greater than 18

                        Conjunctive and patterns 逻辑与匹配


                        // And pattern
                        var person1 = new Person("Zhimin Zhang", 40);
                        var ageInRange = person1 switch {
                        (_, < 18) => "less than 18",
                        ("Zhang Zhimin", _) and (_, >= 18) => "Zhimin Zhang is greater than 18"
                        };
                        Console.WriteLine(ageInRange); // Zhimin Zhang is greater than 18

                        Disjunctive or patterns 逻辑或匹配


                        // Or pattern
                        var person1 = new Person("Zhimin Zhang", 40);
                        var ageInRange = person1 switch {
                        (_, < 18) => "less than 18",
                        (_, 18) or (_, > 18) => "18 or greater"
                        };
                        Console.WriteLine(ageInRange); // 18 or greater

                        Negated not patterns 逻辑非匹配


                        // Not pattern
                        var person1 = new Person("Zhimin Zhang", 40);
                        var meOrNot = person1 switch {
                        not ("Zhimin Zhang", 40) => "Not me!",
                        _ => "Me :-)"
                        };
                        Console.WriteLine(meOrNot); // Me :-)

                        Parenthesized patterns 带括号的优先级匹配


                        // Parenthesized patterns
                        var is10 = new IsNumber(true, 10);
                        var n10 = is10 switch {
                        ((_, > 1 and < 5) and (_, > 5 and < 9)) or (_, 10) => "10",
                        _ => "not 10"
                        };
                        Console.WriteLine(n10); // 10

                        注意, 如果没有匹配到全部的情况, 将会出现异常。

                        新的初始化表达式

                        在C#9.0中,当已创建对象的类型已知时,可以在new表达式中省略该类型。


                        public class MyClass {
                        private List<WeatherObservation> _observations = new();
                        }

                        Point p = new(1, 1);
                        Dictionary<string, int> dict = new();

                        Point[] points = { new(1, 1), new (2, 2), new (3, 3) };
                        var list = new List<Point> {
                        new(1, 1), new(2, 2), new(3, 3)
                        };
                        • 优点: 可以让代码更加简洁;

                        • 缺点: 某些情况下会让代码更难理解;

                        目标类型条件表达式

                        可以隐式转换 null 值, 在 C#9.0 中得到了增强。


                        void TestMethod(int[] list, uint? u) {
                        int[] x = list ?? new int[0];
                        var l = u ?? -1u;
                        }

                        GetEnumerator 扩展

                        可以为任意类型添加一个 GetEnumerator<T> 扩展, 返回一个 IEnumerator<T> 或者 IAsyncEnumerator<T> 实例, 从而在 foreach 循环中使用。


                        public static class Extensions {
                        public static IEnumerator<T> GetEnumerator<T>(this IEnumerator<T> enumerator) => enumerator;
                        }

                        IEnumerator<string> enumerator = new Collection<string> {
                        "A", "B", "C"
                        }.GetEnumerator();

                        foreach (var item in enumerator) {
                        Console.WriteLine(item);
                        }

                        在本地函数上添加标记

                        允许在本地函数上添加标记。


                        static void Main(string[] args) {

                        [Conditional("DEBUG")]
                        static void DoSomething([NotNull] string test) {
                        Console.WriteLine("Do it!");
                        }

                        DoSomething("Doing!");
                        }

                        分部方法扩展

                        在C#9.0中,移除了分部方法的几个限制:

                        1. 必须具有 void 返回类型。

                        2. 不能具有 out 参数。

                        3. 不能具有任何可访问性(隐式 private )。


                        partial class Doing {
                        internal partial bool DoSomething(string s, out int i);
                        }

                        partial class Doing {
                        internal partial bool DoSomething(string s, out int i) {
                        i = 0;
                        return true;
                        }
                        }

                        静态 lambda 表达式

                        从 C#9.0 开始,可以将 static 修饰符添加到 lambda 表达式或 匿名方法 。静态 lambda 表达式类似于 static 局部函数:静态lambda或匿名方法无法捕获局部变量或实例状态。 所述 static 可以防止意外捕获其他变量。

                        lambda 表达式会捕获上下文的变量,不仅会有性能的问题,而且还可能出现错误,比如:


                        int number = 0;

                        Func<string> toString = () => number.ToString(); // number 被自动捕获进 toString 函数中

                        可以在 lambda 表达式前添加 static 关键字, 来解决这个问题:


                        int number = 0;
                        Func<string> toString = static () => number.ToString(); // 这里无法再使用 number ;

                        模块初始化代码

                        可以使用 ModuleInitializerAttribute 为组件 (assembly) 定义初始化代码, 当初始化/加载时执行, 可以类比类的静态构造函数, 但是是组件级别的, 要求如下:

                        • 必须是静态的、无参数的、无返回值的方法;

                        • 不能是范型方法,也不能包含在范型类中;

                        • 不能是私有函数,必须是公开 (public) 或者内部 (internal) 的函数;

                        协变返回类型

                        协变返回类型为重写方法的返回类型提供了灵活性。覆盖方法可以返回从覆盖的基础方法的返回类型派生的类型。这对于记录和其他支持虚拟克隆或工厂方法的类型很有用。 比如:


                        public virtual Person GetPerson() { return new Person(); }

                        public override Person GetPerson() { return new Student(); }

                        在 C# 9.0 中, 可以在子类中返回更加详细的类型:


                        public virtual Person GetPerson() { return new Person(); }

                        public Student Person GetPerson() { return new Student(); }

                        原生整数类型

                        C#9 添加了两个新的整数类型 (nint 和 nunit) , 依赖宿主机以及编译设定。


                        nint nativeInt = 55;
                        Console.WriteLine(nint.MaxValue);

                        // 在 x86 平台上, 输出为 2147483647

                        // 在 x64 平台上, 输出为 9223372036854775807
                        • 优点:可以更好的兼容原生API;

                        • 缺点:缺失平台无关性;

                        跳过本地初始化 (SkipLocalInit)

                        在 C#9.0 中,可以使用 SkipLocalsInitAttribute 来告知编译器不要发射 (Emit) .locals init 标记。


                        [System.Runtime.CompilerServices.SkipLocalsInit]
                        static unsafe void DemoLocalsInit() {
                        int x;
                        // 注意, x 没有初始化, 输出结果不确定;
                        Console.WriteLine(*&x);
                        }
                        • 优点:跳过本地初始化可以提升程序的性能;

                        • 缺点:性能的影响通常不大,建议只在极端情况下才使用这个;

                        函数指针

                        使用 delegate* 可以声明函数指针。


                        unsafe class FunctionPointer {
                        static int GetLength(string s) => s.Length;
                        delegate*<string, int> functionPointer = &GetLength;
                        }

                        public void Test() {
                        Console.WriteLine(functionPointer("test")); // 4;
                        }

                        来源:https://beginor.github.io/2021/02/25/csharp-9-cheatsheet.html

                        标签:c#,9.0,特性
                        0
                        投稿

                        猜你喜欢

                      • android绘制曲线和折线图的方法

                        2021-11-19 02:26:02
                      • 使用sharding-jdbc实现水平分库+水平分表的示例代码

                        2023-11-05 03:21:50
                      • Java关键字this与super详解用法

                        2023-05-28 05:28:04
                      • java网络编程基础知识介绍

                        2023-01-10 20:37:44
                      • Android通过应用程序创建快捷方式的方法

                        2021-07-08 00:41:09
                      • Java中关于字符串的编码方式

                        2023-01-30 04:59:45
                      • 基于java实现画图板功能

                        2022-12-09 07:04:49
                      • Spring Cloud Zuul集成Swagger实现过程解析

                        2021-05-26 12:36:09
                      • Android封装MVP实现登录注册功能

                        2021-06-14 20:45:08
                      • Android中volley封装实践记录(二)

                        2023-05-17 02:37:13
                      • Mybatis-Plus支持GBase8s分页查询的实现示例

                        2021-11-21 14:33:30
                      • Java IO之序列化与反序列化详解

                        2023-05-21 14:59:14
                      • Ubuntu安装jdk8常用方法流程解析

                        2021-12-21 13:40:04
                      • SpringBoot整合EasyExcel实现文件导入导出

                        2021-11-07 17:14:51
                      • springcloud整合seata的实现代码

                        2022-04-26 23:49:49
                      • Java Socket编程服务器响应客户端实例代码

                        2023-08-09 03:25:51
                      • 使用@PropertySource读取配置文件通过@Value进行参数注入

                        2021-11-21 13:21:03
                      • Java接口中尽量避免使用数组

                        2022-07-06 23:24:14
                      • Unity代码实现序列帧动画播放器

                        2023-03-24 23:48:45
                      • springcloud feign传输List的坑及解决

                        2023-06-20 18:31:57
                      • asp之家 软件编程 m.aspxhome.com