C# 9 新特性 —— 补充篇

Intro

前面我们分别介绍了一些 C# 9 中的新特性,还有一些我觉得需要了解一下的新特性,写一篇作为补充。

Top-Level Statements

在以往的代码里,一个应用程序必须要有 Main 方法才能运行,从 C# 9 开始,支持没有 Main 方法的程序,实际编译之后还是会有一个 Main 方法的,使用示例如下:

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

实际编译出来的结果如下:

实际会生成一个没有命名空间的 <Program>$ 的类型,类中定义的有一个名称是 <Main>$ 的静态方法

Improved discards in lambda input parameter

从 C# 7.2 开始,我们可以使用 _ 来代表一个不使用的变量,废弃变量,但是在 lambda 表达式里默认不能有同名的参数名,从 C# 9 开始,支持多个参数同时使用 _ 来表示,如下所示:

Func<int, int, int> constant = (_, _) => 42;

Attributes for local function

从 C# 9 开始,我们可以在局部方法(本地方法)上设置 Attribute

public static void MainTest()
{InnerTest();[MethodImpl(MethodImplOptions.Synchronized)]void InnerTest(){Console.WriteLine(nameof(InnerTest));}
}

Partition methods

在 C# 2.0 之后就支持了分部类,通常分部类会出现在动态代码生成的地方,对于想要将一个类型拆分到多个文件里,我们通常也会考虑用到分部类。

C# 3.0 开始支持了分部方法,但是功能比较弱,使用起来有一些限制:

  • 分部类型各部分中的签名必须匹配。

  • 方法必须返回 void。

  • 不允许使用访问修饰符。分部方法是隐式 private 的。

C# 9 增强了分部方法的支持,分部方法的使用,只能在一个地方有方法体,目前主要是为了 Source Generator 引入了这个语言特性,可以在一个地方定义方法,在另外一个地方实现方法体,示例如下:

partial class PartialMethod
{public static partial void MainTest();static partial void Test1();
}partial class PartialMethod
{public static partial void MainTest(){Test1();Console.WriteLine("Partial method works");}
}

符合 C# 3.0 分部方法规则的允许没有方法体,否则必须要有方法体

ModuleInitializer

Source Generator 除了上面的分部方法之外,还引入了一个 ModuleInitializer 的概念,就像它的名字,模块初始化器,当用到某个模块的时候就会调用对应的 ModuleInitializer 方法进行初始化操作

ModuleInitializer 定义如下:

namespace System.Runtime.CompilerServices
{[AttributeUsage(AttributeTargets.Method, Inherited = false)]public sealed class ModuleInitializerAttribute : Attribute{}
}

使用示例如下:

internal static class ModuleInitializerSample
{/// <summary>/// Initializer for specific module/// /// Must be static/// Must be parameter-less/// Must return void/// Must not be a generic method/// Must not be contained in a generic class/// Must be accessible from the containing module/// </summary>[ModuleInitializer]public static void Initialize(){Console.WriteLine($"{nameof(ModuleInitializerAttribute)} works");}
}

ModuleInitlializer 对应的方法有几个要求

  • 必须是静态方法

  • 不能有方法参数,无参数方法

  • 方法没有返回值,返回类型必须是 void

  • 不能是泛型方法

  • 不能在泛型类中

  • 必须能够被所在模块访问的到(至少是 internal)

来看反编译的代码,可以看到有一个 Module 的类,在这个 Module 类的静态构造方法里会去调用声明为 ModuleInitializer 的方法

Function Pointer

C# 9 支持方法指针,对委托进一步的”C++化“,进一步提升性能,属于非安全代码,使用需开启 unsafe,使用示例如下:

public static unsafe void MainTest()
{delegate*<int, int, int> pointer = &Test;var result = pointer(1, 1);Console.WriteLine(result);
}private static int Test(int num1, int num2)
{Console.WriteLine($"Invoke in {nameof(Test)}, {num1}_{num2}");return num1 + num2;
}

Static Anoymouse Method

C# 9 开始支持在匿名方法或者表达式前声明 static,声明 static 之后就不能使用实例变量,只能使用静态变量,如下所示:

internal class StaticAnonymousMethod
{private readonly int num = 1;public void MainTest(){// anonymous methodAction action = () => { Console.WriteLine(num); };Action action1 = static () => { };// can not access `num`//expressionExpression<Func<int, bool>> expression = i => i > num;Expression<Func<int, bool>> expression1 = static i => i > 1;// can not access `num`}
}

Covariant Return Type

C# 9 开始支持返回类型的 Covariant(协变), 对于 override 方法可返回从重写基方法的返回类型派生的类型。这对于record和其他支持工厂方法的类型会很有用。可以参考下面的使用示例:

internal class CovariantReturnType
{private abstract class Operation{}private abstract class OperationFactory{public abstract Operation GetOperation();}private class AddOperation : Operation{}private class AddOperationFactory : OperationFactory{// 返回类型协变,返回具体的类型而不是抽象类中声明的类型public override AddOperation GetOperation(){return new();}}public static void MainTest(){var factory = new AddOperationFactory();factory.GetOperation();}
}

More

除此之外还有一些小的更新特性,详细可以参考文末给出的官方文档。

Reference

  • https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9

  • https://github.com/WeihanLi/SamplesInPractice/tree/master/CSharp9Sample

C# 9 新特性 —— 补充篇相关推荐

  1. C# 10 新特性 —— 补充篇

    C# 10 新特性 -- 补充篇 Intro 前面已经写了几篇文章介绍 C# 10 新特性的文章,还有一些小的更新 Constant interpolated strings 在之前的版本中,如果想要 ...

  2. JDK8之新特性扩展篇

    转载自 JDK8之新特性扩展篇 BASE64 base64编码解码已经被加入到了jdk8中了. import java.nio.charset.StandardCharsets; import jav ...

  3. Java8新特性整理篇

    文章目录 概述 Lambda表达式 函数式(Functional)接口 Stream API Optional类 概述 Java 8是oracle公司于2014年3月发布,可以看成是自Java 5 以 ...

  4. LINQ学习之旅——准备(C#3.0新特性补充)

    今天主要是对上一节所说的C#3.0的新特性做些补充:对象及集合初始化表达式.匿名类型.局部方法以及查询表达式.这些特性在LINQ中使用也很频繁,尤其是查询表达式. 1.对象初始化表达式允许开发者在一条 ...

  5. ES6 — ES11 新特性一篇通

    一.ES6 1.1.let 变量不能重复声明 有块级作用域 不存在变量提升(必须先声明后使用) 不影响作用域链 <script>//声明变量let a;let b, c, d;let e ...

  6. LMKD浅析(四)——Android Q新特性(QCOM篇)

    前言 在lmkd这个文件上,除了MTK,QCOM也大刀阔斧的在改,导致这两家的基线和AOSP都有一定差异,本篇着重讨论QCOM平台的一些差异的地方,AOSP的晚点再说吧-- 关于MTK平台的介绍,可以 ...

  7. 总结CSS3新特性(选择器篇)

    CSS3新增了嗯- -21个选择器,脚本通过控制台在这里运行; ~: p ~ p {color : red;/*此条规则将用于p后边所有的p...就是除了第一个p的所有p,规则同p:not(:nth- ...

  8. 从C语言到C++的进阶之C++的非类新特性(篇三)

    文章目录 1.从宏到内联函数 2.函数参数的缺省值 3.C++的常量与引用 4.函数重载 5.new运算和delete运算 1.从宏到内联函数 (1)写一个返回最大值的宏定义(C语言) #includ ...

  9. 从C语言到C++的进阶之C++的非类新特性(篇二)

    文章目录 1.输出输入使用"cout<<"和"cin>>" 2.访问外部变量 3.从指针到引用 4.引用与指针的比较 5.返回引用类型的 ...

最新文章

  1. MM32F3277 MicroPython的 mem 函数对于MCU内存访问
  2. HTML5 表单 中
  3. java更新数据库错误就回滚_Java 中对数据库操作时的 回滚
  4. 海洋泡沫结点图完整分析
  5. Android 系统(87)---常见的内存泄漏原因及解决方法
  6. 大学英语四六级写作模板
  7. sap business one 开发_敏捷软件开发实践:估算与计划读书笔记120第18章 估算速度...
  8. 多媒体计算机辅助英语教学,多媒体计算机辅助英语教学初探
  9. word 2010中页码从任意页开始
  10. web安全基础知识-part2
  11. 如何将录音转成文字?
  12. win7电脑蓝屏代码0x00000034
  13. 音乐现场的未来将被NFT门票主宰?
  14. 线阵相机学习笔记(一)
  15. 【遇见大咖】测试界:如何定位自己的角色扮演?
  16. 基于android的远程视频监控系统(已开放源码)
  17. 国内与国外区块链关键技术现状及差异
  18. 前端学习第三十一课(ES6简介和babel的使用)
  19. pta 吃鱼还是吃肉 c语言
  20. 使用javascript和jquery获取类方法

热门文章

  1. iTOP-4412开发板实现3路ADC数模转换驱动例程
  2. 【Tomcat】Tomcat配置与优化(内存、并发、管理)【自己配置】
  3. 解决安装Weblogic domain卡住问题(Primeton BPS)
  4. DOM节点创建(jQuery)
  5. Cannot resolve the collation conflict between SQL_Latin1_General_CP1_CI_AS and Latin1_General_100...
  6. 60佳优秀的 Photoshop 网页制作教程【下篇】
  7. IE6下z-index犯癫不起作用bug的初步研究
  8. 从零开始学习jQuery (九) jQuery工具函数 【转】
  9. 深圳dotnet俱乐部新群
  10. 关于JAVA异常处理的20个最佳实践