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

C#中如何为不可变集合实现集合初始化语法且不破坏不可变性

不可变集合支持集合初始化语法的实现方案

目前有两种成熟的实现方案,可在保障集合不可变性的前提下保留初始化语法的简洁性:


方案1:Builder模式+隐式转换(兼容C# 6及以上所有版本)

核心思路是将可变的Add方法移到嵌套的Builder类中,不可变集合本身不对外暴露任何修改接口,通过隐式转换自动将初始化完成的Builder实例转为不可变集合。

实现代码:

class MyElement
{
    public MyElement() { }
    public MyElement(string label) => Label = label;
    public MyElement(int code, string description) => Label = $"{code}:{description}";
    public string Label { get; set; }
}

class MyImmutableCollection : IEnumerable<MyElement>
{
    private readonly IReadOnlyList<MyElement> _list;

    // 仅内部可用的构造函数
    internal MyImmutableCollection(List<MyElement> list)
    {
        _list = list.AsReadOnly();
    }

    // 仅暴露读操作
    public MyElement this[int index] => _list[index];
    public int Count => _list.Count;

    #region IEnumerable实现
    public IEnumerator<MyElement> GetEnumerator() => _list.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    #endregion

    // 嵌套Builder类,专门用于初始化
    public class Builder : IEnumerable<MyElement>
    {
        private readonly List<MyElement> _list = new();

        // 所有Add重载都放在Builder中
        public void Add(MyElement element) => _list.Add(element);
        public void Add(string label) => Add(new MyElement(label));
        public void Add(int code, string description) => Add(new MyElement(code, description));

        #region IEnumerable实现(满足集合初始化语法要求)
        public IEnumerator<MyElement> GetEnumerator() => _list.GetEnumerator();
        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
        #endregion

        // 隐式转换为不可变集合
        public static implicit operator MyImmutableCollection(Builder builder)
        {
            return new MyImmutableCollection(builder._list);
        }
    }
}

使用效果:

// 和期望的初始化语法几乎一致,最终得到的是完全不可变的集合
MyImmutableCollection collection1 = new MyImmutableCollection.Builder { new MyElement() };
MyImmutableCollection collection2 = new MyImmutableCollection.Builder { "ABC" };
MyImmutableCollection collection3 = new MyImmutableCollection.Builder
{
    { 1, "One" },
    { 2, "Two" }
};

最终得到的MyImmutableCollection实例没有任何公开的修改方法,彻底保障不可变性。


方案2:C# 12+ 集合表达式原生支持

如果项目使用C# 12及以上版本,可以通过CollectionBuilder特性直接获得原生集合表达式的支持,代码更简洁。

实现代码:

using System.Runtime.CompilerServices;

[CollectionBuilder(typeof(MyImmutableCollectionBuilder), nameof(MyImmutableCollectionBuilder.Build))]
class MyImmutableCollection : IEnumerable<MyElement>
{
    private readonly IReadOnlyList<MyElement> _list;

    internal MyImmutableCollection(List<MyElement> list) => _list = list.AsReadOnly();

    // 可选:添加支持多参数的构造函数
    public MyImmutableCollection(params (int code, string description)[] elements)
        : this(elements.Select(x => new MyElement(x.code, x.description)).ToList()) { }

    public MyElement this[int index] => _list[index];
    public int Count => _list.Count;

    public IEnumerator<MyElement> GetEnumerator() => _list.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

// 编译期使用的Builder静态类
static class MyImmutableCollectionBuilder
{
    public static MyImmutableCollection Build(ReadOnlySpan<MyElement> elements)
    {
        return new MyImmutableCollection(elements.ToList());
    }
}

使用效果:

var collection1 = new MyImmutableCollection([new MyElement()]);
var collection2 = new MyImmutableCollection([new MyElement("ABC")]);
var collection3 = new MyImmutableCollection([(1, "One"), (2, "Two")]);

该方案完全基于C#原生特性实现,无额外语法负担,集合本身无任何公开修改接口。


关于你提到的方法级init修饰符提案,目前确实已被撤回,暂无官方特性支持公开Add方法仅在初始化阶段可用,上述两种方案是当前生态下的最优解。

内容的提问来源于stack exchange,提问作者Dmitry Kotleev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 10:24:04