zoukankan      html  css  js  c++  java
  • 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]

  • 相关阅读:
    [LeetCode] Power of Three 判断3的次方数
    [LeetCode] 322. Coin Change 硬币找零
    [LeetCode] 321. Create Maximum Number 创建最大数
    ITK 3.20.1 VS2010 Configuration 配置
    VTK 5.10.1 VS2010 Configuration 配置
    FLTK 1.3.3 MinGW 4.9.1 Configuration 配置
    FLTK 1.1.10 VS2010 Configuration 配置
    Inheritance, Association, Aggregation, and Composition 类的继承,关联,聚合和组合的区别
    [LeetCode] Bulb Switcher 灯泡开关
    [LeetCode] Maximum Product of Word Lengths 单词长度的最大积
  • 原文地址:https://www.cnblogs.com/Rwing/p/csharp-9-0-preview-init-only.html
Copyright © 2011-2022 走看看