假设我正在编写一个应该返回Map的方法.例如:
public Map<String, Integer> foo() {
return new HashMap<String, Integer>();
}
Run Code Online (Sandbox Code Playgroud)
在考虑了一段时间之后,我决定在创建Map之后没有理由修改它.因此,我想返回一个ImmutableMap.
public Map<String, Integer> foo() {
return ImmutableMap.of();
}
Run Code Online (Sandbox Code Playgroud)
我应该将返回类型保留为通用Map,还是应该指定我返回一个ImmutableMap?
从一个方面来说,这就是为什么创建接口的原因; 隐藏实现细节.
另一方面,如果我将这样离开,其他开发人员可能会错过这个对象是不可变的这一事实.因此,我不会实现不可变对象的主要目标; 通过最小化可以更改的对象的数量来使代码更清晰.甚至最糟糕的是,过了一段时间,有人可能会尝试更改此对象,这将导致运行时错误(编译器不会对此发出警告).
.NET 4.5有一个新的命名空间System.Collections.Immutable
此包提供了线程安全的集合,并保证永远不会更改其内容,也称为不可变集合.
我糊涂了.是不是ReadOnlyCollection类已经解决了线程安全问题?为什么要使用ImmutableList呢?
我知道还有一个IReadOnlyList 接口.这并不能隐式解决线程安全问题,因为其他线程可能会通过另一个接口编辑该对象.
什么是ImmutableArray<T>
和之间的区别ImmutableList<T>
,哪个最好使用每个?
我只是从(一个较旧的)Microsoft.Bcl.Immutable
NuGet包切换到System.Collections.Immutable
并且很惊讶在我的项目中找到所有这些新的包依赖项:
System.Collections
System.Diagnostics.Debug
System.Globalization
System.Linq
System.Resources.ResourceManager
System.Runtime
System.Runtime.Extensions
System.Threading
Run Code Online (Sandbox Code Playgroud)
它们被列为NuGet包的依赖项,因此它们有权在那里,但它们显然也已经安装在我的PC和我的目标环境(Azure btw)上,因为它们随框架一起提供.
我已经在我的项目中有大量的软件包,并且如果可能的话,我希望避免由这8个软件包引起的额外开销(并且不会在脚下拍摄自己).
删除这些依赖项是否安全?
我现在是否必须在整个项目中使用这些软件包,因为它们可能与已安装的版本不同,我的项目的某些部分现在可能使用了错误的软件包?(由于某些DLL连接疯狂?)
编辑:只是为了完整性,因为之前有一个评论:依赖项是实际的包(不是名称空间),必须下载,我的目标是使用.NET 4.6进行编译,在VS2015中工作.虽然有些东西已经过时且包装不需要正常加载,但这是完全可能的吗?
在C#中,这两个容器之间的关键区别(在功能或用例方面)是什么?似乎没有任何信息可以在Google上比较这些信息.
System.Collections.ObjectModel.ReadOnlyDictionary System.Collections.Immutable.ImmutableDictionary
我知道ImmutableDictionary是线程安全的.ReadOnlyDictionary也是如此吗?
在不可变性之前,IEnumerable是许多API中的首选接口,因为它具有API对传递对象的实际类型不敏感的优点.
public void DoSomeEnumerationsWithACollection(IEnumerable<Thing> collectionOfThings)
{
foreach (var thing in collectionOfThings) doSomethingWith(thing);
foreach (var thing in collectionOfThings) doSomethingElseWith(thing);
}
Run Code Online (Sandbox Code Playgroud)
当然,至少有两个缺点:
API背后的代码不能依赖collectionOfThings的不变性,可能会遇到"集合修改"异常或遇到其他其他微妙问题.
我们不知道collectionOfThings是真实集合还是简单的延迟查询.如果我们假设它是一个真正的集合,那么我们就不会通过运行多个枚举来降低性能.如果我们假设它是一个延迟查询并且它实际上是一个真实的集合,那么将其转换为本地列表或其他冻结集合会产生不必要的成本,尽管它确实有助于保护我们免受第一个问题的影响(执行"ToList"操作时仍存在竞争条件).显然,我们可以编写少量代码来检查这一点,并尝试做"正确的事情",但这是令人讨厌的额外混乱.
我必须承认,除了使用命名约定之外,我从未找到过令人满意的模式来解决这个问题.尽管存在缺点,但是实用的方法似乎是IEnumerable是传递集合的最低摩擦方法.
现在,随着不可变的收藏,情况得到了很大改善......
public void DoSomeEnumerationsWithACollection(ImmutableList<Thing> collectionOfThings)
{
Run Code Online (Sandbox Code Playgroud)
不再存在收集修改的风险,并且对多个枚举的性能影响没有任何歧义.
但是,我们显然失去了API的灵活性,因为我们现在必须传入一个ImmutableList.如果我们的客户端有其他类型的可枚举不可变集合,则必须将其复制到ImmutableList中才能被使用,即使我们想要做的只是枚举它.
理想情况下,我们可以使用类似的界面
public void DoSomeEnumerationsWithACollection(IImmutableEnumerable<Thing> collectionOfThings)
Run Code Online (Sandbox Code Playgroud)
但是,当然,除了约定之外,接口不能强制执行不可变性等语义.
使用基类可能会起作用
public void DoSomeEnumerationsWithACollection(ImmutableEnumerableBase<Thing> collectionOfThings)
Run Code Online (Sandbox Code Playgroud)
除了它被认为是创建未密封的不可变类的不良形式,以免子类引入可变性.无论如何,这还没有在BCL中完成.
或者我们可以继续在API中使用IEnumerable并使用命名约定来明确我们的代码依赖于传递的不可变集合.
所以...我的问题是在传递不可变集合时哪些模式被认为是最好的?是ImmutableList "新的IEnumerable "一旦我们开始使用不可变性,或是否有更好的办法?
更新
IReadOnlyCollection(由Yuval Itzchakov建议)是对IEnumerable的明显改进,但仍然没有完全保护消费者免受集合中不受控制的变化的影响.值得注意的是,Roslyn代码库大量使用不变性(主要通过ImmutableArray)并且在将这些传递给其他方法时似乎使用显式类型,尽管有几个位置将ImmutableList传递给接受IEnumerable的方法.
我和Nuget有一个令人困惑的问题.我有很多项目声称在1.3.0版本上安装了System.Collections.Immutable 但是如果我在所有引用中看到dll的版本,我看到版本1.2.1.0
当我用JustDecompile打开DLL时,我看到了
它声明DLL版本确实是1.2.1.0但已安装在目录包\ System.Collections.Immutable.1.3.0中
典型的packages.config文件将包含
<package id="System.Collections.Immutable" version="1.3.0" targetFramework="net452" />
Run Code Online (Sandbox Code Playgroud)
和csproj是
<Reference
Include="System.Collections.Immutable, Version=1.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)packages\System.Collections.Immutable.1.3.0\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll</HintPath>
<Private>True</Private>
</Reference>
Run Code Online (Sandbox Code Playgroud)
如果我尝试从库中打开下载的nuget包并使用nuget package explorer打开,我会得到
我听说.NET System.Collections.Immutable
集合是作为平衡二叉树实现的,以便Dictionary
通过使用整数值GetHashCode
作为排序键来满足它们的不变性约束,甚至传统上模拟散列表的集合.
如果我有一个类型,它是便宜生成一个散列码,以及对于便宜的比较(如string
或int
),我不关心我收集整理的烦躁,这将是有意义的喜欢ImmutableSortedDictionary
,因为底层数据结构是否仍然排序?
我有这样的树结构数据:
[{
id: 54,
name:123,
children: [{
id: 54,
name:123,
children: [{
id: 154,
name:1234,
children []...
}]
}]
}, {
...
}]
Run Code Online (Sandbox Code Playgroud)
我正在使用Angular 2.据我所知,无论何时输入发生变化,您的变化检测策略都会发生变化onPush
.
为了优化树结构更新(例如,在嵌套级别切换节点或更改此类节点的任何属性),我使用了Immutable.
Immutable如何帮助我优化我的更新?我读到,当数据发生变化时,Immutable会重用旧数据中的引用来构造新对象.
如何有效地使用不可变数据结构来更新嵌套级别的节点?
keyPath
任何节点.id
属性值,可用于查询树数据(但是如何?)withMutations
API,但还有其他有效的方法吗?深层复制所有内容,然后修改新构造的对象:
var newState = deepClone(oldState) // deep copy everything and construct a new object
newState.nodes.forEach(node => {
if(node.id == 54) {
node.id = 789;
}
})
Run Code Online (Sandbox Code Playgroud)我想要实现的内容:
var newState = …
Run Code Online (Sandbox Code Playgroud)