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.为什么使用不可变集合 不可变对象有很多优点,包括: 当对象被不可信的库调用时,不可变形式是安全的:不可变对象被多个线程调用时,不存在竞态条件问题不可变集合不 ...
随机推荐
- ASP.NET_各个币种之间的汇率转换(实时)使用Yahoo汇率。
近期开发支付平台的时候有运用到各国的实时汇率之间的转换问题,于是在往上找了很多相关资料,以下就是一些参考网址: 1.提供API接口的网站:https://www.showapi.com:这个网站有提供 ...
- 8.仿阿里云虚拟云服务器的FTP(包括FTP文件夹大小限制)
平台之大势何人能挡? 带着你的Net飞奔吧!:http://www.cnblogs.com/dunitian/p/4822808.html#iis 原文:http://dnt.dkill.net/Ar ...
- 多个Img标签之间的间隙处理方法
1.多个标签写在一行 <img src="/i/eg_tulip.jpg" alt="郁金香" height="100px"/> ...
- gulp详细入门教程
本文链接:http://www.ydcss.com/archives/18 gulp详细入门教程 简介: gulp是前端开发过程中对代码进行构建的工具,是自动化项目的构建利器:她不仅能对网站资源进行优 ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(72)-微信公众平台开发-消息处理
系列目录 前言 Senparc.Weixin.MP SDK提供了MessageHandler消息处理类 在作者的Wiki中也详细说明了如何定义这个类,下面我们来演示,消息的回复,及效果 了解Messa ...
- ASP.NET从零开始学习EF的增删改查
ASP.NET从零开始学习EF的增删改查 最近辞职了,但是离真正的离职还有一段时间,趁着这段空档期,总想着写些东西,想来想去,也不是很明确到底想写个啥,但是闲着也是够 ...
- Mac OS、Ubuntu 安装及使用 Consul
Consul 概念(摘录): Consul 是 HashiCorp 公司推出的开源工具,用于实现分布式系统的服务发现与配置.与其他分布式服务注册与发现的方案,比如 Airbnb 的 SmartStac ...
- JS的内建函数reduce
@(js) reduce函数,是ECMAScript5规范中出现的数组方法.在平时的工作中,相信大家使用的场景并不多,一般而言,可以通过reduce方法实现的逻辑都可以通过forEach方法来变相的实 ...
- winform异步加载数据到界面
做一个学习记录. 有两个需求: 1.点击按钮,异步加载数据,不卡顿UI. 2.把获取的数据加载到gridview上面. 对于需求1,2,代码如下: public delegate void ShowD ...
- 我大中华微软MVP中国区人才库
刘海峰:国内知名微软开源技术网站51Aspx 创始人,十年以上的Asp.net从业经验,微软MSDN特约讲师.Teched讲师.ImagineCup大赛评委.人大出版社研修班特约讲师,曾多次受邀访问美 ...