Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/319.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 什么';这是跨API传递不可变集合的最佳模式_C#_.net_Immutability_Immutable Collections_Immutablelist - Fatal编程技术网

C# 什么';这是跨API传递不可变集合的最佳模式

C# 什么';这是跨API传递不可变集合的最佳模式,c#,.net,immutability,immutable-collections,immutablelist,C#,.net,Immutability,Immutable Collections,Immutablelist,在不变性之前,IEnumerable是许多API中的go-to接口,因为这具有API对传递对象的实际类型不敏感的优点 public void DoSomeEnumerationsWithACollection(IEnumerable<Thing> collectionOfThings) { foreach (var thing in collectionOfThings) doSomethingWith(thing); foreach (var thing in col

在不变性之前,IEnumerable是许多API中的go-to接口,因为这具有API对传递对象的实际类型不敏感的优点

public void DoSomeEnumerationsWithACollection(IEnumerable<Thing> collectionOfThings)
{ 
   foreach (var thing in collectionOfThings) doSomethingWith(thing);
   foreach (var thing in collectionOfThings) doSomethingElseWith(thing);
}
public void dosomeEnumerations with acollection(IEnumerable collectionof things)
{ 
foreach(var thing in collectionof things)dosomething with(thing);
foreach(var thing in Collectionof Things)dosomethingelewith(thing);
}
当然,这至少有两个缺点:

  • API背后的代码不能依赖于collectionOfThings的不变性,可能会遇到“collection-modified”异常或其他微妙问题

  • 我们不知道collectionOfThings是真正的收集还是只是一个延迟查询。如果我们假设它是一个真实的集合,而不是,那么运行多个枚举可能会降低性能。如果我们假设它是一个延迟查询,并且实际上是一个真实的集合,那么将其转换为本地列表或其他冻结集合会产生不必要的成本,尽管它确实有助于防止我们遇到第一个问题(在执行“ToList”操作时仍然存在竞争条件)。显然,我们可以编写少量代码来检查这一点,并尝试做“正确的事情”,但这是恼人的额外混乱

  • 我必须承认,除了使用命名约定之外,我从未找到一个令人满意的模式来解决这个问题。务实的方法似乎是,IEnumerable是传递集合的摩擦最小的方法,尽管有缺点

    现在,使用不可变集合,情况有了很大改善

    public void DoSomeEnumerationsWithACollection(ImmutableList<Thing> collectionOfThings)
    { 
    
    public void dosomeEnumerations with acollection(不可变列表内容集合)
    { 
    
    不再存在修改集合的风险,也不再存在多个枚举对性能影响的模糊性

    然而,我们显然已经失去了API的灵活性,因为我们现在必须传入一个不可变列表。如果我们的客户机有其他类型的可枚举不可变集合,则必须将其复制到不可变列表,以便使用,即使我们只想枚举它

    理想情况下,我们可以使用像

    public void DoSomeEnumerationsWithACollection(IImmutableEnumerable<Thing> collectionOfThings)
    
    public void dosomeEnumerations with acollection(IIMMutableEnumeratable collectionof things)
    
    但当然,接口不能像不变性一样强制语义,除非通过约定

    使用基类可能有效

    public void DoSomeEnumerationsWithACollection(ImmutableEnumerableBase<Thing> collectionOfThings)
    
    public void dosomeEnumerations with acollection(ImmutableEnumerableBase things集合)
    
    除了创建未密封的不可变类被认为是不好的形式,以免子类引入易变性。在任何情况下,BCL中都没有这样做

    或者我们可以继续在API中使用IEnumerable,并使用命名约定来明确我们的代码依赖于要传入的不可变集合

    所以…我的问题是,在传递不可变集合时,什么模式被认为是最好的?一旦我们开始使用不可变性,ImmutableList是“新的IEnumerable”,还是有更好的方法

    更新

    IReadOnlyCollection(以下由Yuval Itzhakov建议)是对IEnumerable的一个明显改进,但仍然不能完全保护消费者免受集合中不受控制的更改。值得注意的是,Roslyn代码库大量使用了不变性(主要通过不变性数组)并且在将它们传递给其他方法时似乎使用显式类型,尽管有几个位置将不可变列表传递给接受IEnumerable的方法

    在传递不可变集合时,哪些模式被认为是最好的


    我想你的问题的答案是
    IReadOnlyCollection
    ,它将在.NET 4.6中传播。通过传递一个只读集合,你既可以保持不可变性,也可以使用实现该接口的常规集合。

    在大量使用不可变集合几年后,我已经习惯了几乎在所有地方都使用ImmutableArray。这并不能满足API灵活性的最初要求,但在实践中,我很少使用ImmutableList或其他类似列表的结构,而且当我这样做时,调用ImmutableArray来跨接口获取数据通常不会有太大开销。

    尽管事实证明存在ImmutableListBCL中的接口(IImmutableList,IImmutableSet)似乎没有可枚举不可变类通用的接口。您通常可以检查某个对象是否为真实集合,是否实现了ICollectionThank Yuval。这似乎是一个不错的建议,尽管我想知道它在强制不可变性方面到底有多好。ReadOnlyCollection显然实现了IReadOnlyCol选择接口,但MSDN文档()明确指出,ReadOnlyCollection只是可变对象的包装。因此,使用IReadOnlyCollection似乎仍然允许可变对象在API中传递。@NeilMacMullen我认为传递am IROC很明显,可以说“此shoyld仅用作读取副本”。有人真的会尝试改变这样一个接口吗?IROC的提供者正在有效地保护自己,防止消费者无意中改变集合。但是,消费者并没有得到同样的保证。从这个意义上说,它比为消费者和提供者提供保证的完全不可变的API弱呃。@NeilMacMullen不是真的。即使你提供了一个不变的列表,也不意味着消费者不能有效地为自己制作一个可变的coopy。从某种意义上说,消费者会