.NET不可变集合已经正式发布

微软基础类库(Base Class Library)团队已经完成了.NET不可变集合的正式版本,但不包括ImmutableArray。与其一起发布的还包括针对其它不可变对象类型的设计指南。

如果你需要在多个线程中安全地共享集合,并且允许每个线程在需要时对其内容进行改变。这种场景就是不可变集合所设计的初衷。只读集合在使用时需要复制集合中的全部内容,而新的不可变集合可以以一种更高性能的方式从一个现有集合中进行创建。

使用不可变集合需要特别当心,因为你很容易错误地写成“list.Add(item)”,而正确的方法是“list = list.Add(item)”。甚至编译器也可能产生类似的错误,这也是为什么不可变集合不支持构造函数的原因。考虑以下代码:

list = new ImmutableList<int> {1, 2, 3};

在编译后会产生以下代码:

temp = new ImmutableList(); temp.Add(1); temp.Add(2) temp.Add(3) list = temp;

由于3次Add方法的结果都被丢弃,最终整个集合包含的项数目为0,而不是期望中的3。

不可变对象指南

Immo Lendwerth建议,当你在创建自己的不可变对象时,在其中加入适当的WithXxx方法。对简单的对象来说,为每一个属性创建一个WithXxx方法即可。当属性值需要变化时,该方法会返回当前对象的一个拷贝。

如果某属性代表了一个结合,那么这种模式就需要一点变化。以下这段代码来自Immo的发布声明

class Order
{
    public Order(IEnumerable<OrderLine> lines)
    {
        Lines = lines.ToImmutableList();
    }
    public ImmutableList<OrderLine> Lines { get; private set; }
    public Order WithLines(IEnumerableOrderLine> value)
    {
        return Object.ReferenceEquals(Lines, value)
            ? this
            : new Order(value);
    }
}

如你所见,WithLines方法可接受任意IEnumerable。因此你可以传递一个新创建的ImmutableList对象,或者是某个LINQ表达式的结果。这种方式已经足以满足需求了,不过他还建议提供某些辅助方法:

class Order
{
    //...
    public Order AddLine(OrderLine value)
    {
        return WithLines(Lines.Add(value));
    }
    public Order RemoveLine(OrderLine value)
    {
        return WithLines(Lines.Remove(value));
    }
    public Order ReplaceLine(OrderLine oldValue, OrderLine newValue)
    {
        return oldValue == newValue
                ? this
                : WithLines(Lines.Replace(oldValue, newValue));
    }
}

ImmutableArray被移除

由于性能方面的原因,ImmutableArray从最终的发布版本中被移除。其原因是:为了满足内存性能指标,ImmutableArray必须设计成一个值对象,并且为了保持值对象的语义,ImmutableArray的默认实例必须表现为一个空数组形式。不幸的是,为了达到这一点,对空值的检测(null check)会使得C#无法移除对数组边界的检测,而这一点是为达到良好CPU性能的一个重要考虑事项。

由于ImmutableArray类对于Roslyn编译器项目非常重要,设计者曾考虑删除会导致性能问题的空值检测功能,但又因此产生了另外的问题,Immo这样写道

由于所有的值类型都有一个自动产生的默认构造函数,它会将该值类型初始化为它的默认状态,而ImmutableArray<T>的默认值是空,它的底层数组实现则为null。因此,AddRange方法的实现会因为NullReferenceException的产生而崩溃。 这一问题还表现在其它一些地方,由于ImmutableArray<T>实现了某些集合接口(例如IEnumerable和IReadOnlyList),因此你可以把它传递给某些接受这种接口的方法。由于这种接口引用是非空的,使用者在调用它的方法或者属性时不会考虑到有可能产生NullReferenceException。

基础类库团队并未放弃这个项目,他们还在研究其它设计方式,以争取让ImmutableArray重新亮相。

查看英文原文:.NET Immutable Collections Ready for Production

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏zaking's

js算法初窥04(算法模式01-递归)

14520
来自专栏xingoo, 一个梦想做发明家的程序员

剑指OFFER之最小的K个数(九度OJ1371)

题目描述: 输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。 输入: 每个测试案例包括...

21290
来自专栏mini188

学习笔记:Hashtable和HashMap

学了这么些天的基础知识发现自己还是个门外汗,难怪自己一直混的不怎么样。但这样的恶补不知道有没有用,是不是过段时间这些知识又忘了呢?这些知识平时的工作好像都是随拿...

22880
来自专栏Ryan Miao

Java中对List去重, Stream去重

问题 当下互联网技术成熟,越来越多的趋向去中心化、分布式、流计算,使得很多以前在数据库侧做的事情放到了Java端。今天有人问道,如果数据库字段没有索引,那么应该...

1.3K70
来自专栏程序员的SOD蜜

JavaScript的“原型甘露”

今天跟朋友讨论JS的面向对象编程问题,想起了原来曾经看过一篇文章,但是看过很久想不起来了,用了很多关键词,终于用“悟透JavaScript  面向对象”这两个关...

27780
来自专栏玄魂工作室

Python黑帽编程2.1 Python编程哲学

本节的内容有些趣味性,涉及到很多人为什么会选择Python,为什么会喜欢这门语言。我带大家膜拜下Python作者的Python之禅,然后再来了解下Python的...

33570
来自专栏java学习

Java每日一题3_关于Java

ArrayList:有序,可重复;底层使用数组,查询快,增删慢;线程不安全,效率高;容量不足时扩增为当前容量*1.5 + 1;

9910
来自专栏黑泽君的专栏

HashMap与Hashtable的区别是面试中经常遇到的一个问题。

HashMap与Hashtable的区别是面试中经常遇到的一个问题。这个问题看似简单,但如果深究进去,也能了解到不少知识。本文对两者从来源,特性,算法等多个方面...

35530
来自专栏我杨某人的青春满是悔恨

Swift API 设计指南(上)

本文翻译自苹果官方文档:Swift API Design Guidelines,如有错漏,欢迎指出。

12330
来自专栏HansBug's Lab

算法模板——并查集 2(支持快速即时查询本连通块内容,纯原创!)

实现功能:输入N,现在有N个数;接下来输入任意行,如果是"1 x y"则表示把x和y所在的块合并;如果是"2 x"则表示输出x所在的块的全部内容 原理:其实主要...

32660

扫码关注云+社区

领取腾讯云代金券