首页 > 其他分享 >Immutable(不可变)集合

Immutable(不可变)集合

时间:2022-11-28 14:06:37浏览次数:73  
标签:不可 value OrderLine public Collections 集合 Order Immutable

不可变集合,顾名思义就是说集合是不可被修改的。集合的数据项是在创建的时候提供,并且在整个生命周期中都不可改变。

为什么要用immutable对象?immutable对象有以下的优点:

  1. 对不可靠的客户代码库来说,它使用安全,可以在未受信任的类库中安全的使用这些对象
  2. 线程安全的:immutable对象在多线程下安全,没有竞态条件
  3. 不需要支持可变性, 可以尽量节省空间和时间的开销. 所有的不可变集合实现都比可变集合更加有效的利用内存 (analysis)
  4. 可以被使用为一个常量,并且期望在未来也是保持不变的

immutable对象可以很自然地用作常量,因为它们天生就是不可变的对于immutable对象的运用来说,它是一个很好的防御编程(defensive programming)的技术实践。

微软.NET团队已经正式发布了不可变集合,可以通过Nuget添加,包括了下面的不可变集合:

System.Collections.Immutable.ImmutableArray

System.Collections.Immutable.ImmutableArray<T>

System.Collections.Immutable.ImmutableDictionary

System.Collections.Immutable.ImmutableDictionary<TKey,TValue>

System.Collections.Immutable.ImmutableHashSet

System.Collections.Immutable.ImmutableHashSet<T>

System.Collections.Immutable.ImmutableList

System.Collections.Immutable.ImmutableList<T>

System.Collections.Immutable.ImmutableQueue

System.Collections.Immutable.ImmutableQueue<T>

System.Collections.Immutable.ImmutableSortedDictionary

System.Collections.Immutable.ImmutableSortedDictionary<TKey,TValue>

System.Collections.Immutable.ImmutableSortedSet

System.Collections.Immutable.ImmutableSortedSet<T>

System.Collections.Immutable.ImmutableStack

System.Collections.Immutable.ImmutableStack<T>

MSDN的文档参考 ​​https://msdn.microsoft.com/zh-cn/library/system.collections.immutable.aspx​​ ,怎么使用呢?我们来看一个例子,假设你已经建立了一个计费系统,你需要一个不可变的设计,在多线程操作的情况下不需要担心数据损坏。例如,你需要通过一个辅助线程打印数据的一个快照,这种方式避免阻塞用户的编辑操作,允许用户继续编辑而不影响打印。

可变的数据模型是这样:

class Order
{
    public Order()
    {
        Lines = new List<OrderLine>();
    }

    public List<OrderLine> Lines { get; private set; }
}

class OrderLine
{
    public int Quantity { get; set; }
    public decimal UnitPrice { get; set; }
    public float Discount { get; set; }

    public decimal Total
    {
        get
        {
         return Quantity * UnitPrice * (decimal) (1.0f - Discount);
        }
    }
}

下面我们把它转换为不可变的设计:

class OrderLine
{
    public OrderLine(int quantity, decimal unitPrice, float discount)
    {
        Quantity = quantity;
        UnitPrice = unitPrice;
        Discount = discount;
    }

    public int Quantity { get; private set; }

    public decimal UnitPrice { get; private set; }

    public float Discount { get; private set; }

    public decimal Total
    {
        get
        {
         return Quantity * UnitPrice * (decimal) (1.0f - Discount);
        }
    }
}

这种新设计要求您创建一个订单,每当任何属性值变化创建一个新实例。您可以通过添加 WithXxx 方法,使您可以更新单个属性而无需显式调用构造函数:

class OrderLine
{
    // ...

    public OrderLine WithQuantity(int value)
    {
        return value == Quantity
                ? this
                : new OrderLine(value, UnitPrice, Discount);
    }

    public OrderLine WithUnitPrice(decimal value)
    {
        return value == UnitPrice
                ? this
                : new OrderLine(Quantity, value, Discount);
    }

    public OrderLine WithDiscount(float value)
    {
        return value == Discount
                ? this
                : new OrderLine(Quantity, UnitPrice, value);
    }
}

这使得不可变使用起来比较简单:

OrderLine apple = new OrderLine(quantity: 1, unitPrice: 2.5m, discount: 0.0f);

OrderLine discountedAppled = apple.WithDiscount(.3f);

现在让我们看看我们如何落实订单的不变性。Lines 属性已经是只读的但它指的是可变对象。因为它是一个集合,它可以容易地通过简单地将它替换 ImmutableList <T>转换:

class Order
{
    public Order(IEnumerable<OrderLine> lines)
    {
        Lines = lines.ToImmutableList();
    }

    public ImmutableList<OrderLine> Lines { get; private set; }

    public Order WithLines(IEnumerable<OrderLine> value)
    {
        return Object.ReferenceEquals(Lines, value)
            ? this
            : new Order(value);
    }
}

这种设计有一些有趣的属性:

• 该构造函数接受 IEnumerable <T>,允许传递任何集合中。

• 我们使用 ToImmutableList() 扩展方法,将转换为 ImmutableList <OrderLine>。如果该实例已经是不可变的列表,它会简单地转换而不是创建一个新的集合。

• 该 WithLines() 方法遵循 我们的订单公约,如果新的列表和当前列表是相同的就可以避免创建一个新的实例。

我们还可以加一些便利的方法来使它更易于更新订单行:

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));
    }
}

增补订单的代码看起来是这样子:

OrderLine apple = new OrderLine(quantity: 1, unitPrice: 2.5m, discount: 0.0f);
Order order = new Order(ImmutableList.Create(apple));

OrderLine discountedApple = apple.WithDiscount(discount);
Order discountedOrder = order.ReplaceLine(apple, discountedApple);

这种设计的好处是,它尽可能避免了不必要的对象创建。例如,当折扣的值等于 0.0 f,即时没有折扣,,discountedApple 和 discountedOrder 引用现有实例的苹果和订单。

这是因为:

1.apple.WithDiscount() 将返回苹果的现有实例,因为新的折扣是相同折扣属性的当前值。

2.order.ReplaceLine() 如果两个参数都相同,将返回现有实例。

我们不变的集合其他操作遵循这种最大化重用。例如,将订单行添加到 1000 的订单行的订单与 1,001 订单行不会创建整个的新列表。相反,它将重用现有列表一大块。这是可能的因为列表内部结构是为一棵树,允许共享不同实例的节点。

这里有两个视频介绍可变性集合:

 Immutable Collections for .NET


 ​​Inner workings of immutable collections​


不可变集合的系列博客推荐:

​Exploring the .NET CoreFX Part 9: Immutable Collections and the Builder ​

​Exploring the .NET CoreFX Part 13: ImmutableList is an AVL Tree​

​Exploring the .NET CoreFX Part 14: Inside Immutable Collections​

标签:不可,value,OrderLine,public,Collections,集合,Order,Immutable
From: https://blog.51cto.com/shanyou/5891069

相关文章

  • 对集合List<Map<String,Object>>进行一个分页
    需求要对集合List<Map<String,Object>>进行一个分页:/***利用subList方法进行分页**@paramlist分页数据*@parampagesize页面大......
  • Java集合之ArrayList和LinkedList
    1ArrayList和LinkedListArrayList和LinkedList是平常经常用到的两种集合1.1对于两者的底层ArrayList的底层是使用的数组,而LinkedList的底层使用双向链表1//Array......
  • 集合
    集合集合概述数组其实就是一个集合。集合实际上就是一个容器。可以来容纳其它类型的数据。集合为什么说在开发中使用较多?集合是一个容器,是一个载体,可以一次容纳多个对......
  • Scala学习(除列表集合等)
    Idea配置安装 进入idea后,选取file中的setting选项,在plugins中安装scala,之后重启idea。 右键点击项目,选取AddFrameworkSupport,并选取对应的scala版本  新建......
  • 学生管理系统-通过集合-遍历学生类
    packagecom.集合;importjava.util.ArrayList;importjava.util.Scanner;publicclass学生管理系统{publicstaticvoidmain(String[]args){System.out.......
  • 拓端tecdat|R语言编程指导对混合分布中的不可观测与可观测异质性因子分析
    R语言对混合分布中的不可观测与可观测异质性因子分析今天上午,在课程中,我们讨论了利率制定中可观察和不可观察异质性之间的区别(从经济角度出发)。为了......
  • PS2023神经元滤镜离线安装教程,解决PS神经滤镜灰色不可用!
    PS2023神经元滤镜离线安装包,这是一款专门在Photoshop上使用的多功能滤镜工具,英文名为:NeuralFilters,它为用户提供了一个完整的滤镜库,用户可以通过这里各种各样的滤镜制作出......
  • springboot集合efk搭建日志平台
    springboot继承efk实现日志收集1.安装es和kibana我使用的云服务器centos7,2核+4G内存,跑起来内存使用率50%左右建议使用最低配置和我一样,1+2的配置kibana应该跑不起来,......
  • 集合初认识
    /*需求:1.学会导入集合的包2.学会给集合赋值集合对象.add(赋给集合的值)3.学会给集合指定的位置赋值集合对象.add(索引值,赋给集合的值)*/  /*当插入......
  • 集合的常用的方法
    /*需求:学会使用集合的常用方法1.A.remove()删除指定集合中的值,并且返回ture或者false,括号里写你想要删除的值2.A.remove()删除指定索引处集合中的值,并且返回被删除的集......