You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#9中init-only属性与只读属性有何区别?求解认知误区

搞懂C# 9的init-only属性 vs 传统只读属性

嘿,这个问题问得特别戳痛点——刚接触C# 9的开发者几乎都会混淆这俩特性,咱来掰扯清楚你忽略的点!

首先得肯定你的理解:你写的Thingy类里的Name和Description是传统的get-only只读属性,确实只能在构造函数内部赋值,对象创建后完全不可修改,这部分你说的一点没错。那init-only属性到底多了啥?

核心区别就是初始化的灵活性——它保留了不可变性,但给你提供了更多创建对象的方式,不用被构造函数绑死。

1. 对象初始化器的支持

传统只读属性必须通过构造函数传参来赋值,如果你想给类加几个可选属性,就得写一堆重载构造函数,特别麻烦。但init-only属性允许你用对象初始化器直接赋值,比如:

class Thingy {
    // 把原来的get;改成get; init;
    public string Name { get; init; }
    public string Description { get; init; }
    public string? ExtraInfo { get; init; } // 新增可选属性
    public override string ToString() => $"{Name}: {Description} {ExtraInfo ?? ""}";
}

然后创建对象的时候,你可以灵活选择要赋值的属性:

var thingy1 = new Thingy { Name = "Test1", Description = "Full info" };
var thingy2 = new Thingy { Name = "Test2", Description = "Basic info", ExtraInfo = "Some extra" };

不用写两个构造函数,是不是清爽多了?

2. 继承与记录类型的适配

在继承场景里,子类如果想给父类的只读属性赋值,必须在子类构造函数里显式调用父类的构造函数传参。但用init-only属性的话,子类可以直接在对象初始化器里给父类属性赋值:

class BaseThing { public string Id { get; init; } }
class DerivedThing : BaseThing { public string Name { get; init; } }

// 直接初始化父类+子类属性
var derived = new DerivedThing { Id = "123", Name = "Test" };

另外,C# 9的记录类型(record)默认用init-only属性,配合with表达式可以轻松实现非破坏性修改——创建新对象的同时修改部分属性,原对象保持不变:

record Thingy { public string Name { get; init; } public string Description { get; init; } }

var original = new Thingy { Name = "Test", Description = "Old desc" };
var updated = original with { Description = "New desc" };
// original还是原来的样子,updated是新对象

这是传统只读属性完全做不到的。

总结一下

特性赋值时机灵活性
传统get-only属性仅构造函数内部低,依赖构造函数重载
init-only属性构造函数/对象初始化器/with表达式高,不用写冗余构造函数

你之前忽略的,就是init-only属性在不破坏不可变性的前提下,给对象创建带来的灵活性——它不是替代传统只读属性,而是补充了更现代的初始化方式,适配C#后续的记录类型、非破坏性修改等特性。

内容的提问来源于stack exchange,提问作者Mark A. Donohoe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 20:47:52