• C# 9.0 新特性预览


    C# 9.0 新特性预览 - init-only 属性

    前言

    随着 .NET 5 发布日期的日益临近,其对应的 C# 新版本已确定为 C# 9.0,其中新增加的特性(或语法糖)也已基本锁定,本系列文章将向大家展示它们。

    目录

    [C# 9.0 新特性预览 - 类型推导的 new]
    [C# 9.0 新特性预览 - 空参数校验]
    [C# 9.0 新特性预览 - 顶级语句]
    [C# 9.0 新特性预览 - init-only 属性]
    [C# 9.0 新特性预览 - Record 类型]
    [C# 9.0 新特性预览 - 模式匹配的改善]
    [C# 9.0 新特性预览 - 源代码生成器]
    [C# 9.0 新特性预览 - 其他小的变化]

    只初始化 setter (Init Only Setters)

    这个特性允许创建只初始化(init only)的属性和索引器,使得 C# 中的不可变模型更加灵活。

    背景

    在此之前,我们创建实体类/POCO类/DTO类等等模型类的时候,都期望属性只读不允许从外部修改,会将属性的 setter 设为私有或者干脆不设置 setter,例如:

    public class Person
    {
        public string Name { get; private set; }
        // OR
        //public string Name { get; }
    }
    

    再添加一个拥有全部属性作为签名的构造方法:

    ...
    public Person(string name)
    {
        this.Name = name;
    }
    ...
    

    这样做虽然可以达到目的,但是带来了两个问题
    1.假如属性增多,会带来工作量的成倍增加,以及不易维护
    2.无法使用对象初始化器(object initializers)

    var person = new Person
    {
        Name = "Rwing" // Compile Error 
    };
    

    在这个情况下,init 关键字应运而生了。

    语法

    语法很简单,只需要将属性中的 set 关键字替换为 init 即可:

    public string Name { get; init; }
    

    以上代码会被大致翻译为:

    private readonly string _name;
    public string Name
    {
        get { return _name; }
        set { _name = value;}
    }
    

    可以看到,与 set 的区别是,init 会为背后的字段添加 readonly 关键字。
    这样我们就可以去掉一堆属性的构造方法转而使用对象初始化器了,并且达到了不可变的目的。

    var person = new Person
    {
        Name = "Rwing"
    };
    // 初始化后无法再次修改
    person.Name = "Foo"; // Error: Name is not settable
    

    这一语法,有很多场景需要配合同样在 C# 9.0 中新增的 record 类型使用。

    哪些情况下可以被设置

    • 通过对象初始化器
    • 通过 with 表达式
    • 在自身或者派生类的构造方法中
    • 在标记为 init 的属性中
    • 在特性(attribute)类的命名参数属性中

    以上场景不难理解,但是值得一提的是,只有 get 的属性是不可以派生类的构造方法中赋值的,但是 init 可以:

    class Base
    {
        public bool Foo { get; init; }
        public bool Bar { get; }
    }
    
    class Derived : Base
    {
        Derived()
        {
            Foo = true;
            Bar = true; // ERROR
        }
    }
    

    此外有一种例外, 在以上场景中的 lambda 或本地函数中,也不允许被设置,例如:
    原因也很简单,lambda 或本地函数在编译后已经不在构造函数中了。

    public class Class
    {
        public string Property { get; init; }
        
        Class()
        {
            System.Action a = () =>
            {
                Property = null; // ERROR
            };
            local();
            void local()
            {
                Property = null; // ERROR
            }
        }
    }
    

    参考

    [Proposal: Init Only Setters]
    [InitOnlyMemberTests.cs]

  • 相关阅读:
    flask ajax
    python 符合条件跳过下一次循环
    python使用openpyxl excel 合并拆分单元格
    等价类划分法
    python 同级目录包导入问题,使用"."错误
    django:查询,反向查询
    Python实现程序执行次数的计数
    python 2x SSH通道连接服务器读取数据库和中文编码问题
    Python for 循环中使用append()添加可变元素,前面的值被覆盖,循环中内存应用地址不变
    以概率列表选择对应元素,轮盘概率选择Python实现
  • 原文地址:https://www.cnblogs.com/Rwing/p/csharp-9-0-preview-init-only.html
Copyright © 2020-2023  润新知