繁体   English   中英

当项目最初必须是可变的时,返回不可变集合

[英]Returning an immutable collection when the items must be mutable initially

一般情况的一个例子:

public class Validator
{
   // ...
   public ReadOnlyCollection<InvalidContainer> ContainersUnderMinimum
   {
      get { return _containersUnderMinimum.AsReadOnly(); }
   }
}

public class InvalidContainer
{
   // ...
   public int LowestVolume { get; set; }
}

上面的Validator类在其构造函数中获取其他项的集合,然后将无效项添加到内部List。 每个容器都有许多子容器(想想一个试管架),并且该类希望找到最低容量。 当找不到项(管)时,构造函数将添加到列表中,并在找到项时更新现有列表对象。

问题是Validator想要返回一个只读的不可变对象集合,但是对象(InvalidContainers)在构造后必须是可变的,这样才能(基本上)累积值。

重构使用接口(IInvalidContainer)会导致头痛,因为通用集合无法转换为基类型的集合。

有什么好的模式或做法可以解决这个问题?

编辑:澄清一下,目的是让属性值(集合)不可变。 我知道ReadOnlyCollection只强制集合的不变性,而不是集合项。 通常我会使项目不可变,但我不能在这个(和类似的)情况下。 但是,我只希望在构造Validator类时突变项。 防止呼叫者进行不明智的铸造不是设计目标; 目标是避免使用可设置的公共属性诱惑调用者。

编辑:为了清晰起见改变了标题。

编辑:这是重构版本(基于LBushkin和递归的建议):

public IEnumerable<IInvalidContainer> ContainersUnderMinimum
{
   get
   {
      return _containersUnderMinimum.Cast<IInvalidContainer>();
   }
}

如果我正确理解您的问题,您希望返回一组不可变类型,但在内部保留一个可变集合。 这样做的典型方法是为您的类型创建一个不可变的基类型(或接口)并返回它。

您可以将项目转换为该类型(弱变形控件的形式),也可以创建包装器对象并返回它们(强大的控件类型)。 包装对象的创建成本可能更高 - 但它们可以防止外部代码只是能够执行类型转换来绕过不变性。 顺便说一下,这是ReadOnlyCollection<T>用于返回不可变集合的机制。

要克服集合类型具有有限的转换支持这一事实,您可以使用LINQ返回不可变类型的不可变副本:

_containersUnderMinimum.Cast<IInvalidContainer>().ToList().AsReadOnly()

这会创建集合的副本 - 但它可能足够好 - 如果您不需要集合来反映运行时的更改。

此外,请注意ReadOnlyCollection不要求(或强制)集合元素的不变性。 相反,它会阻止接收器添加或删除元素 - 仍然可以更改集合中的现有元素。

如果你的可变对象只能通过方法改变,我建议你在你的mutable类型中包含一个引用,如果是非null,它将标识一个封装相同数据的不可变类型的实例。 你的可变类型应该包含一个创建不可变副本的方法; 该方法应该生成并缓存一个新的不可变对象,如果它尚未持有对它的引用。 否则它应该返回缓存的引用。 任何变异方法都应使immutable-object引用无效。 使用这种方法,人们应该能够避免必须重复复制从未变异的对象。

实际上,可以转换泛型集合:

ReadOnlyCollection<IInvalidContainer> result = 
   _containersUnderMinimum.Cast<IInvalidContainer>().ToList().AsReadOnly();

但是,这并不能阻止消费者重新投射元素。

也许我误解了,但ReadOnlyCollection只暗示集合是ReadOnly,而不是对象本身......

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM