2011 年 1 月 6 日更新:
信不信由你,我继续将此接口合并到我已经启动的开源库 Tao.NET中。我写了一篇博客文章来解释这个库的IArray<T>
接口,它不仅解决了我最初在这个问题中提出的问题(一年前?!),而且还提供了一个协变索引接口,这是 BCL 中非常缺乏的(在我看来)。
问题(简而言之):
我问为什么 .NET 有IList<T>
, 它实现ICollection<T>
并因此提供了修改列表的方法(Add
,Remove
等),但不提供任何中间接口,例如在IArray<T>
不修改任何列表的情况下通过索引提供随机访问。
编辑 2010 年 1 月 21 日下午 2:22 EST:
在对 Jon Skeet 的原始答案的评论中(他在其中质疑人们多久需要一次合同,例如IArray<T>
),我提到类的Keys
和Values
属性分别是和,Jon 回答说:SortedList<TKey, TValues>
IList<TKey>
IList<Value>
但在这种情况下,它被声明为 IList 并且您知道只使用索引器。. . . 我同意,它并不是非常优雅——但它实际上并没有给我带来任何痛苦。
这是合理的,但我会回答说它不会给你带来任何痛苦,因为你只是知道你做不到。但是您知道的原因并不是从代码中可以清楚地看出;就是你有SortedList<TKey, TValue>
上课的经验。
如果我这样做,Visual Studio 不会给我任何警告:
SortedList<string, int> mySortedList = new SortedList<string, int>();
// ...
IList<string> keys = mySortedList.Keys;
keys.Add("newkey");
这是合法的,根据IList<string>
. 但我们都知道,它会导致异常。
纪尧姆也提出了一个恰当的观点:
好吧,接口并不完美,但开发人员可以在调用 Add/Remove/Set 之前检查 IsReadOnly 属性...
同样,这是合理的,但是:这不是让你觉得有点迂回吗?
假设我定义了一个接口如下:
public interface ICanWalkAndRun {
bool IsCapableOfRunning { get; }
void Walk();
void Run();
}
现在,还假设我将实现这个接口作为一种常见的做法,但仅限于它的Walk
方法;在许多情况下,我会选择设置IsCapableOfRunning
并false
抛出NotSupportedException
...Run
然后我可能有一些看起来像这样的代码:
var walkerRunners = new Dictionary<string, ICanWalkAndRun>();
// ...
ICanWalkAndRun walkerRunner = walkerRunners["somekey"];
if (walkerRunner.IsCapableOfRunning) {
walkerRunner.Run();
} else {
walkerRunner.Walk();
}
我是疯了,还是这种破坏了名为 的接口的目的ICanWalkAndRun
?
原帖
我发现在 .NET 中,当我设计一个具有通过索引(或返回索引集合的方法等)提供随机访问的集合属性的类时,不应该或不能通过添加 /删除 items,如果我想在 OOP 方面“做正确的事”并提供一个接口以便我可以在不破坏 API 的情况下更改内部实现,我必须使用IList<T>
.
看来,标准方法是使用一些IList<T>
明确定义方法的实现Add
,Insert
等等 - 通常通过执行以下操作:
private List<T> _items;
public IList<T> Items {
get { return _items.AsReadOnly(); }
}
但我有点讨厌这个。如果另一个开发人员正在使用我的类,并且我的类具有 type 的属性IList<T>
,并且接口的整个想法是:“这些是一些可用的属性和方法”,我为什么要抛出一个NotSupportedException
(或任何情况下)当他/她试图做一些根据界面应该完全合法的事情?
我觉得实现一个界面并明确定义它的一些成员就像开一家餐馆并将一些项目放在菜单上——也许在菜单的一些不起眼、容易错过的部分,但仍然在菜单上——根本不可用。
似乎应该有一个IArray<T>
接口,通过索引提供非常基本的随机访问,但不添加/删除,如下所示:
public interface IArray<T> {
int Length { get; }
T this[int index] { get; }
}
然后IList<T>
可以实现ICollection<T>
和IArray<T>
添加它的IndexOf
,Insert
和RemoveAt
方法。
当然,我总是可以只编写这个接口并自己使用它,但这对所有未实现它的预先存在的 .NET 类没有帮助。(是的,我知道我可以编写一个包装器来接收任何内容IList<T>
并吐出一个IArray<T>
, 但是......认真吗?)
有没有人知道为什么接口System.Collections.Generic
是这样设计的?我错过了什么吗?是否有一个令人信服的论点反对我所说的关于明确定义成员的方法的问题IList<T>
?
我并不想显得自大,好像我比设计 .NET 类和接口的人更了解;这对我来说没有意义。但我已经准备好承认有很多我可能没有考虑到。