zoukankan      html  css  js  c++  java
  • 一探即将到来的 C# 10

    前言

    本来因为懒不想写这篇文章,但是不少人表示有兴趣,于是最后决定还是写一下。

    .NET 6 最近几个预览版一直都在开发体验(如 hot reload、linker 等)、平台支持(如 Android、iOS 等)、工具链(如 crossgen2、PGO 工具和 wasm 的 AOT 等)、JIT(如 LSRA、Jump threading、PGO 和 guarded devirtualization 以及使 struct 保持在寄存器上等)、GC(如 Regions 等)以及 BCL(如 TimeOnly、DateOnly 以及 Json DOM 等)方面做改进,然而却一直没有公布 C# 10 的任何内容,即使在 Build 2021 大会上也没有提及这方面内容。然而实际上不少特性的实现已经接近尾声了,那么让我们提前来看看 C# 10 可以为我们带来什么东西。

    当然,不是所有下面列出的特性都一定会进入 C# 10,也可能会和本文有所出入,我在每一个特性后面加了一个百分比表示最终实装的可能性,仅供参考。

    Backing Fields(60%)

    相信不少人在编写属性的时候,因为自动属性不能满足自己的需求于是不得不改回手动实现属性,这个时候总是会想“如果能不用手动写字段的定义就好了”,现在这个梦想成真了:

    private int myInt;
    public int MyInt { get => myInt; set => myInt = value; }
    

    C# 10 中新增了一个 field,当使用它时会自动为属性创建字段定义,不需要再手动定义字段了,因此也叫做半自动属性。

    public int MyInt { get => field; set => field = value; }
    

    Record Structs(100%)

    Records 此前只支持 class,但是现在同样支持 struct 啦,于是你可以定义值类型的 record,避免不必要的堆内存分配:

    record struct Point(int X, int Y);
    

    with on Anonymous Objects(80%)

    此前 with 只能配合 records 使用,但是现在它被扩展到了匿名对象上,你可以通过 with 来创建匿名对象的副本并且修改它的值啦:

    var foo = new { A = 1, B = "test", C = 4.4 };
    var bar = foo with { A = 3 };
    Console.WriteLine((bar.A, bar.B, bar.C)); // (3, test, 4.4)
    

    Global Usings(80%)

    此前 using 语句的生效范围是单个文件的,如果你想使用一些 namespace,或者定义一系列的类型别名在整个项目内使用,那么你就需要这样:

    using System.Linq;
    using static System.Math;
    using i32 = System.Int32;
    using i64 = System.Int64;
    

    然后在每个文件中重复一遍。但是现在不需要了,你可以定义全局的 using 了:

    global using System.Linq;
    global using static System.Math;
    global using i32 = System.Int32;
    global using i64 = System.Int64;
    

    然后在整个项目中就都可以用了。

    File Scoped Namespace(90%)

    C# 10 开始你将能够在文件顶部指定该文件的 namespace,而不需要写一个 namespace 然后把其他代码都嵌套在大括号里面,毕竟绝大多数情况下,我们在写代码时一个文件里确实只会写一个 namespace,这样可以减少一层嵌套也是很不错的:

    namespace MyProject;
    
    class MyClass
    {
        // ...
    }
    

    如果采用这样的写法,每一个文件将只能声明一个 namespace。

    Constant Interpolated String(100%)

    顾名思义,常量字符串插值:

    const string a = "foo";
    const string b = $"{a}_bar"; // foo_bar
    

    常量字符串插值将在编译时完成。

    Lambda Improvements(100%)

    C# 10 大幅度改进了 lambda,扩展了使用场景,并改进了一系列的推导,提出自然委托类型,还函数上升至 first-class。

    支持 Attributes

    f = [Foo] (x) => x; // 给 lambda 设置
    f = [return: Foo] (x) => x; // 给 lambda 返回值设置
    f = ([Foo] x) => x; // 给 lambda 参数设置
    

    支持显示指定返回值类型

    此前 C# 的 lambda 返回值类型靠推导,C# 10 开始允许在参数列表最前面显示指定 lambda 类型了:

    f = int () => 4;
    

    支持 ref 等修饰

    f = ref int (ref int x) => ref x; // 返回一个参数的引用
    

    First-class Functions

    方法可以被隐式转换到 Delegate,使得函数上升至 first-class。

    Delegate f = 1.GetHashCode; // Func<int>
    object g = 2.ToString; // object(Func<string>)
    var s = (int x) => x; // Func<int, int>
    

    将函数作为变量,然后传给另一个函数的参数:

    void Foo(Func<int> f)
    {
        Console.WriteLine(f());
    }
    
    int Bar()
    {
        return 5;
    }
    
    var baz = Bar;
    Foo(baz);
    

    Natural Delegate Types

    lambda 现在会自动创建自然委托类型。

    可以用 var 来创建委托了:

    var f = () => 1; // Func<int>
    var g = string (int x, string y) => $"{y}{x}"; // Func<int, string, string>
    var g = "test".GetHashCode; // Func<int>
    

    调用 lambdas

    得益于上述改进,创建的类型明确的 lambda 可以直接调用了。

    var zero = ((int x) => x)(0); // 0
    

    Caller Expression Attribute(80%)

    现在,CallerArgumentExpression 这个 attribute 终于有用了。借助这个 attribute,编译器会自动填充调用参数的表达式字符串,例如:

    void Foo(int value, [CallerArgumentExpression("value")] string? expression = null)
    {
        Console.WriteLine(expression + " = " + value);
    }
    

    当你这样调用时:

    Foo(4 + 5);
    

    会输出 4 + 5 = 9。这对测试极其有用,因为你可以输出 assert 的原表达式了:

    static void Assert(bool value, [CallerArgumentExpression("value")] string? expr = null)
    {
        if (!value) throw new AssertFailureException(expr);
    }
    

    default 支持解构(100%)

    default 现在支持解构了,因此可以给 tuples 直接赋值。

    (int a, int b, int c) = default; // (0, 0, 0)
    

    List Patterns(100%)

    Pattern Matching 的最后一块版图:list patterns,终于补齐了。

    void Foo(List<int> list)
    {
        switch (list)
        {
            case [4]:
                Console.WriteLine("长度为 4");
                break;
            case { 1, 2, 3 }:
                Console.WriteLine("元素是 1, 2, 3");
                break;
            case { 1, 2, ..var x, 5 }:
                Console.WriteLine($"前两个元素是 1, 2,最后一个元素是 5,倒数第二个元素是 {x}");
                break;
            default:
                Console.WriteLine("其他");
        }
    }
    

    同样的,该 pattern 也是 recursive 的,因此你可以嵌套其他 patterns。

    除了上述 switch statements 的用法,在 if 以及 switch expressions 等地方也同样可用,例如:

    void Foo(List<int> list)
    {
        var result = list switch
        {
            [4] => ...,
            { 1, 2, 3 } => ...,
            { 1, 2, ..var x, 5 } => ...,
            _ => ...
        };
    }
    

    Abstract Static Member in Interfaces(100%)

    C# 10 中,接口可以声明抽象静态成员了,.NET 的类型系统正式具备 virtual static dispatch 能力。

    例如,你想定义一个可加而且有零的接口 IMonoid

    interface IMonoid<T> where T : IMonoid<T>
    {
        abstract static T Zero { get; }
        abstract static T operator+(T l, T r);
    }
    

    然后可以对其进行实现,例如这里的 MyInt

    public class MyInt : IMonoid<MyInt>
    {
        public MyInt(int val) { Value = val; }
        
        public static MyInt Zero { get; } = new MyInt(0);
        public static MyInt operator+(MyInt l, MyInt r) => new MyInt(l.Value + r.Value);
        
        public int Value { get; }
    }
    

    然后就能写出一个方法对 IMoniod<T> 进行求和了,这里为了方便写成扩展方法:

    public static class IMonoidExtensions
    {
        public static T Sum<T>(this IEnumerable<T> t) where T : IMonoid<T>
        {
            var result = T.Zero;
            foreach (var i in t) result += i;
            return result;
        }
    }
    

    最后调用:

    List<MyInt> list = new() { new(1), new(2), new(3) };
    Console.WriteLine(list.Sum().Value); // 6
    

    这个特性同样也会对 .NET BCL 做出改进,会新增诸如 IAddable<T>INumeric<T> 的接口,并为适用的已有类型实现。

    总结

    以上就是在 C# 10 的大部分新特性介绍了,虽然不保证最终效果和本文效果一致,但是也能看到一个大概的方向。

    从 interface 的改进上我们可以看到一个好的预兆:.NET 终于开始动类型系统了。2008 年至今几乎没有变过的 CTS 显然逐渐不能适应语言发展的需要,而 .NET 团队也明确给出了信息表明要在 C# 11 前后对类型系统集中进行改进,现在只是一个开始,相信不久之后也将能看到 traits、union types、bottom types 和 HKT 等的实装。

    出处:https://www.cnblogs.com/hez2010/p/csharp-10-in-advance.html

    您的资助是我最大的动力!
    金额随意,欢迎来赏!
    款后有任何问题请给我留言。

    如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的推荐按钮。
    如果,您希望更容易地发现我的新博客,不妨点击一下绿色通道的关注我。(●'◡'●)

    如果你觉得本篇文章对你有所帮助,请给予我更多的鼓励,求打             付款后有任何问题请给我留言!!!

    因为,我的写作热情也离不开您的肯定支持,感谢您的阅读,我是【Jack_孟】!

  • 相关阅读:
    Powered by .NET Core 进展0815:第5次发布尝试(Windows部署)团队
    峰回路转:去掉 DbContextPool 后 Windows 上的 .NET Core 版博客表现出色团队
    做梦也没有想到:Windows 上的 .NET Core 版博客系统表现更糟糕团队
    全网最详细的zkfc启动以后,几秒钟以后自动关闭问题的解决办法(图文详解)
    全网最详细的HBase启动以后,HMaster进程启动了,几秒钟以后自动关闭问题的解决办法(图文详解)
    全网最详细的启动或格式化zkfc时出现java.net.NoRouteToHostException: No route to host ... Will not attempt to authenticate using SASL (unknown error)错误的解决办法(图文详解)
    全网最详细的HA集群的主节点之间的双active,双standby,active和standby之间切换的解决办法(图文详解)
    全网最详细的启动zkfc进程时,出现INFO zookeeper.ClientCnxn: Opening socket connection to server***/192.168.80.151:2181. Will not attempt to authenticate using SASL (unknown error)解决办法(图文详解)
    全网最详细的再次或多次格式化导致namenode的ClusterID和datanode的ClusterID之间不一致的问题解决办法(图文详解)
    执行bin/hdfs haadmin -transitionToActive nn1时出现,Automatic failover is enabled for NameNode at bigdata-pro02.kfk.com/192.168.80.152:8020 Refusing to manually manage HA state的解决办法(图文详解)
  • 原文地址:https://www.cnblogs.com/mq0036/p/14857562.html
Copyright © 2011-2022 走看看