Immutable(不可变)集合
不可变集合,顾名思义就是说集合是不可被修改的。集合的数据项是在创建的时候提供,并且在整个生命周期中都不可改变。
为什么要用immutable对象?immutable对象有以下的优点:
- 对不可靠的客户代码库来说,它使用安全,可以在未受信任的类库中安全的使用这些对象
- 线程安全的:immutable对象在多线程下安全,没有竞态条件
- 不需要支持可变性, 可以尽量节省空间和时间的开销. 所有的不可变集合实现都比可变集合更加有效的利用内存 (analysis)
- 可以被使用为一个常量,并且期望在未来也是保持不变的
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
Immutable(不可变)集合的更多相关文章
- Immutable(不可变)集合
Immutable(不可变)集合 不可变集合,顾名思义就是说集合是不可被修改的.集合的数据项是在创建的时候提供,并且在整个生命周期中都不可改变. 为什么要用immutable对象?immutable对 ...
- java代码之美(4)---guava之Immutable(不可变)集合
Immutable(不可变)集合 一.概述 guava是google的一个库,弥补了java语言的很多方面的不足,很多在java8中已有实现,暂时不展开.Collections是jdk提供的一个工具类 ...
- java代码(4)---guava之Immutable(不可变)集合
Immutable(不可变)集合 一,概述 guava是google的一个库,弥补了java语音的很多方面的不足,很多在java8中已有实现,暂时不展开,Collections是jdk提供的一个工 ...
- Guava学习笔记:Immutable(不可变)集合
不可变集合,顾名思义就是说集合是不可被修改的.集合的数据项是在创建的时候提供,并且在整个生命周期中都不可改变. 为什么要用immutable对象?immutable对象有以下的优点: 1.对不可靠的客 ...
- Guava Immutable 不可变集合
Immutable是为了创建不可变集合使用,不可变集合在很多情况下能提高系统性能.一般使用 .of()或者.builder()<>().put().build()初始化创建不可变集合
- Guava集合--Immutable(不可变)集合
所谓不可变集合,顾名思义就是定义了之后不可修改的集合. 一.为什么要使用不可变集合 不可变对象有很多优点,包括: 当对象被不可信的库调用时,不可变形式是安全的: 不可变对象被多个线程调用时,不存在竞态 ...
- 不可变集合 Immutable Collections
例子 public static final ImmutableSet<String> COLOR_NAMES = ImmutableSet.of( "red", &q ...
- [Guava官方文档翻译] 7. Guava的Immutable Collection(不可变集合)工具 (Immutable Collections Explained)
我的技术博客经常被流氓网站恶意爬取转载.请移步原文:http://www.cnblogs.com/hamhog/p/3538666.html ,享受整齐的排版.有效的链接.正确的代码缩进.更好的阅读体 ...
- .NET不可变集合已经正式发布
微软基础类库(Base Class Library)团队已经完成了.NET不可变集合的正式版本,但不包括ImmutableArray.与其一起发布的还包括针对其它不可变对象类型的设计指南. 如果你需要 ...
- 003-guava 集合-不可变集合
一.概述 二.使用 2.1.不可变集合 1.为什么使用不可变集合 不可变对象有很多优点,包括: 当对象被不可信的库调用时,不可变形式是安全的:不可变对象被多个线程调用时,不存在竞态条件问题不可变集合不 ...
随机推荐
- wepack+sass+vue 入门教程(二)
六.新建webpack配置文件 webpack.config.js 文件整体框架内容如下,后续会详细说明每个配置项的配置 webpack.config.js直接放在项目demo目录下 module.e ...
- Azkaban源码学习笔记
1. ConnectorParams (interface): 定义了各种常量参数,没有声明任何方法. 2. ExecutorServlet.java类 2.1 继承类HttpServlet和接口 ...
- 关于 Chrome 浏览器中 onresize 事件的 Bug
我在写插件时用到了 onresize 事件,在反复地测试后发现该事件在 Chrome 及 Opera(内核基本与 Chrome 相同,以下统称 Chrome)浏览器打开时就会执行,这种情况也许不能算作 ...
- SignalR系列续集[系列8:SignalR的性能监测与服务器的负载测试]
目录 SignalR系列目录 前言 也是好久没写博客了,近期确实很忙,嗯..几个项目..头要炸..今天忙里偷闲.继续我们的小系列.. 先谢谢大家的支持.. 我们来聊聊SignalR的性能监测与服务器的 ...
- .NET Core 首例 Office 开源跨平台组件(NPOI Core)
前言 最近项目中,需要使用到 Excel 导出,找了一圈发现没有适用于 .NET Core的,不依赖Office和操作系统限制的 Office 组件,于是萌生了把 NPOI 适配并移植到 .NET C ...
- 使用Oracle官方巡检工具ORAchk巡检数据库
ORAchk概述 ORAchk是Oracle官方出品的Oracle产品健康检查工具,可以从MOS(My Oracle Support)网站上下载,免费使用.这个工具可以检查Oracle数据库,Gold ...
- redis 学习笔记(1)
redis持久化 snapshot数据快照(rdb) 这是一种定时将redis内存中的数据写入磁盘文件的一种方案,这样保留这一时刻redis中的数据镜像,用于意外回滚.redis的snapshot的格 ...
- ASP.NET Core project.json imports 是什么意思?
示例代码: "frameworks": { "netcoreapp1.0.0": { "imports" : "portable- ...
- ASP.NET 5 RC1 升级 ASP.NET Core 1.0 RC2 记录
升级文档: Migrating from DNX to .NET Core Migrating from ASP.NET 5 RC1 to ASP.NET Core 1.0 RC2 Migrating ...
- 基于AOP的MVC拦截异常让代码更优美
与asp.net 打交道很多年,如今天微软的优秀框架越来越多,其中微软在基于mvc的思想架构,也推出了自己的一套asp.net mvc 框架,如果你亲身体验过它,会情不自禁的说‘漂亮’.回过头来,‘漂 ...