背景故事
我有一个包含项目列表的viewmodel。每个项目都有一个州;如果要从列表中删除以前存在的项目,它将保留在列表中,但会被标记为已删除,并且很明显,当用户单击“保存”时,它将被删除。
列表本身绑定到一个在IBindingList
上运行的UI控件。当用户通过UI控件添加和删除行时,UI控件会调用IList.Add
和IList.RemoveAt
。
问题
由于我想保留预先存在的行并以不同方式标记它们,我有两个选择:
我IList.RemoveAt
的实施可以简单地将项目标记为已删除并将其保留在列表中,并且无法引发ListChanged
删除事件。这看起来最简单,最干净,但我想知道是否存在API期望实际删除了索引。想象一下像while (list.Count != 0) { Process(list[0]); list.RemoveAt(0); }
这样的通用循环显然我必须避免这种情况,但是避免它的需要似乎是一种糟糕的耦合。我是否会对API犯罪?或者那段代码只是可以忽略的坏代码,因为它做了太多的假设?
我的实现可能允许删除发生并引发事件,然后将项目标记为已删除,重新插入项目并引发插入事件。这不会损害合同,因为可以理解列表可能会被多个组件变异,并且事件会使每个人保持同步。然而,它是额外的UI闪烁,感觉有点hacky。
我可以修改UI控件(它是第三方但可扩展),以便在列表中查找ISoftDelete
接口。如果接口存在,则调用ISoftDelete.SetDeleted(int index, bool)
而不是调用RemoveAt
。这似乎相当干净,但它的额外复杂性不仅在列表API中,而且在每个UI控件中都需要了解此列表。这种分离是否值得花费?如果RemoveAt
被调用会发生什么 - 该行将从视线中消失,这不是一个好的场景,所以它必须抛出异常或借用#2的解决方法。
修改
奇怪!看看IList.RemoveAt(int index)
with CONTRACTS_FULL
- 它有以下内容:
void IList.RemoveAt(int index)
{
//Contract.Requires(index >= 0);
//Contract.Requires(index < ((IList)this).Count);
//Contract.Ensures(((IList)this).Count == Contract.OldValue(((IList)this).Count) - 1); // Not threadsafe
}
注意'not threadsafe'评论!这是否意味着如果可能的话,他们会取消注释该行?但基于索引的列表中的任何内容都不是线程安全的。