为什么.NET 中没有 IArray(T) 接口? [英] Why is there no IArray(T) interface in .NET?

查看:16
本文介绍了为什么.NET 中没有 IArray(T) 接口?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

2011 年 1 月 6 日更新:

信不信由你,我继续将这个界面整合到一个我已经开始的开源库中,淘网.我 写了一篇博文 解释了这个库的 IArray 接口,它不仅解决了我最初在这个问题中提出的问题(一年前?!)而且还提供了一个协变索引接口,这是 BCL 中非常缺乏的(在我看来).

<小时>

问题(简​​而言之):

我问为什么.NET有IList,它实现了ICollection,因此提供了修改列表的方法(AddRemove 等),但不提供任何中间接口,例如 IArray 以提供无需任何列表修改的索引随机访问.

<小时>

编辑 2010 年 1 月 21 日下午 2:22(美国东部标准时间):

在对 Jon Skeet 的原始回答的评论中(他质疑人们多久需要一份诸如 IArray 之类的合同),我提到了 Keys<SortedList 类的/code> 和 Values 属性是 IListIList,分别是 Jon 回复的:

<块引用>

但在这种情况下,它被声明为IList 并且您知道只使用索引器....这不是很大优雅,我同意 - 但事实并非如此实际上让我感到任何痛苦.

这是合理的,但我会说它不会给你带来任何痛苦,因为你只是知道你做不到.但是原因你知道不是从代码中可以清楚地看出;这是您对 SortedList 类的经验.

如果我这样做,Visual Studio 不会给我任何警告:

SortedListmySortedList = new SortedList();//...IList<字符串>键 = mySortedList.Keys;键.添加(新键");

根据IList,这是合法的.但是我们都知道,它会导致异常.

Guillaume 也提出了一个恰当的观点:

<块引用>

嗯,界面并不完美但开发人员可以检查 IsReadOnly调用前的属性添加/删除/设置...

同样,这是合理的,但是:这是否让您觉得有点迂回?

假设我定义了一个接口如下:

公共接口 ICanWalkAndRun {bool IsCapableOfRunning { 获取;}无效步行();无效运行();}

现在,假设我将实现此接口作为一种常见做法,但仅限于它的 Walk 方法;在许多情况下,我会选择将 IsCapableOfRunning 设置为 false 并在 Run...

然后我可能有一些看起来像这样的代码:

var walkerRunners = new Dictionary();//...ICanWalkAndRun walkerRunner = walkerRunners["somekey"];如果(walkerRunner.IsCapableOfRunning){walkerRunner.Run();} 别的 {walkerRunner.Walk();}

我是疯了,还是这违背了一个名为ICanWalkAndRun的接口的目的?

<小时>

原帖

我发现在 .NET 中非常奇怪,当我设计一个具有集合属性的类时,该类提供通过索引(或返回索引集合的方法等)随机访问,但不应该或无法通过添加/删除项目进行修改,如果我想以 OOP 方式做正确的事"并提供一个接口,以便我可以在不破坏 API 的情况下更改内部实现,我必须使用 <代码>IList.

标准方法似乎是采用 IList 的一些实现,该实现明确定义了方法 AddInsert等——通常通过执行以下操作:

私有列表_项目;公共 IList<T>项目 {得到 { 返回 _items.AsReadOnly();}}

但我有点讨厌这个.如果另一个开发人员正在使用我的类,并且我的类具有 IList 类型的属性,并且接口的整个想法是:这些是一些可用的属性和方法",当他/她试图做一些根据界面应该完全合法的事情时,我为什么要抛出 NotSupportedException(或任何可能的情况)?

我觉得实现一个接口并明确定义它的一些成员就像开一家餐馆并把一些项目放在菜单上——也许是在一些晦涩难懂、容易错过的部分菜单,但菜单上——根本就永远不可用.

似乎应该有一个类似于 IArray 的接口,它通过索引提供非常基本的随机访问,但没有添加/删除,如下所示:

公共接口 IArray{int长度{得到;}T this[int index] { get;}}

然后IList可以实现ICollectionIArray并添加它的IndexOfInsertRemoveAt 方法.

当然,我总是可以自己编写这个接口并使用它,但这对所有没有实现它的预先存在的 .NET 类没有帮助.(是的,我知道我可以编写一个包装器,它接受任何 IList 并吐出一个 IArray,但是……真的吗?)>

有谁知道为什么 System.Collections.Generic 中的接口是这样设计的?我错过了什么吗?对于我在明确定义 IList 的成员的方法方面的问题,是否有令人信服的论据反对?

我不是想听起来自大,好像我比设计 .NET 类和接口的人更了解;对我来说没有意义.但我已经准备好承认有很多我可能没有考虑到的.

解决方案

设计问题并不总是非黑即白.

一方面是针对每种情况的精确接口,这使得实际实现接口的整个过程非常痛苦.

另一个是少数(er)多用途接口,它们并不总是完全被实现者支持,但使许多事情变得更容易,例如传递相似但不会获得精确"中分配的相同接口的实例界面"设计.

所以 BCL 设计师选择了第二种方式.有时我也希望接口少一点多用途,特别是对于集合和 C#4 接口协/逆变特性(它不能应用于大多数集合接口,除了 IEnumerable<> 因为它们包含两个协以及逆变部分).

此外,令人遗憾的是,诸如字符串和原始类型之类的基类不支持某些接口,例如 ICharStream(对于字符串,可用于正则表达式等以允许使用除 string<之外的其他来源)/code> 实例用于模式匹配)或 IArithmetic 用于数字基元,以便通用数学成为可能.但我想所有框架都有一些弱点.

Update 2011-Jan-06:

Believe it or not, I went ahead and incorporated this interface into an open source library I've started, Tao.NET. I wrote a blog post explaining this library's IArray<T> interface, which not only addresses the issues I originally raised in this question (a year ago?!) but also provides a covariant indexed interface, something that's sorely lacking (in my opinion) in the BCL.


Question (in short):

I asked why .NET has IList<T>, which implements ICollection<T> and therefore provides methods to modify the list (Add, Remove, etc.), but doesn't offer any in-between interface such as IArray<T> to provide random access by index without any list modification.


EDIT 2010-Jan-21 2:22 PM EST:

In a comment to Jon Skeet's original answer (in which he questioned how often one would have any need for a contract such as IArray<T>), I mentioned that the Keys and Values properties of the SortedList<TKey, TValues> class are IList<TKey> and IList<Value>, respectively, to which Jon replied:

But in this case it's declared to be IList and you know to just use the indexers. . . . It's not hugely elegant, I agree - but it doesn't actually cause me any pain.

This is reasonable, but I would respond by saying that it doesn't cause you any pain because you just know you can't do it. But the reason you know isn't that it's clear from the code; it's that you have experience with the SortedList<TKey, TValue> class.

Visual Studio isn't going to give me any warnings if I do this:

SortedList<string, int> mySortedList = new SortedList<string, int>();

// ...

IList<string> keys = mySortedList.Keys;
keys.Add("newkey");

It's legal, according to IList<string>. But we all know, it's going to cause an exception.

Guillaume made an apt point as well:

Well, the interfaces aren't perfect but a dev can check the IsReadOnly property before calling Add/Remove/Set...

Again, this is reasonable, BUT: does this not strike you as a bit circuitous?

Suppose I defined an interface as follows:

public interface ICanWalkAndRun {
    bool IsCapableOfRunning { get; }

    void Walk();
    void Run();
}

Now, suppose as well that I made it a common practice to implement this interface, but only for its Walk method; in many cases, I would opt to set IsCapableOfRunning to false and throw a NotSupportedException on Run...

Then I might have some code that looked like this:

var walkerRunners = new Dictionary<string, ICanWalkAndRun>();

// ...

ICanWalkAndRun walkerRunner = walkerRunners["somekey"];

if (walkerRunner.IsCapableOfRunning) {
    walkerRunner.Run();
} else {
    walkerRunner.Walk();
}

Am I crazy, or is this kind of defeating the purpose of an interface called ICanWalkAndRun?


Original Post

I find it very peculiar that in .NET, when I am designing a class with a collection property that provides random access by index (or a method that returns an indexed collection, etc.), but should not or cannot be modified by adding/removing items, and if I want to "do the right thing" OOP-wise and provide an interface so that I can change the internal implementation without breaking the API, I have to go with IList<T>.

The standard approach, it seems, is to go with some implementation of IList<T> that explicitly defines the methods Add, Insert, etc. -- typically by doing something like:

private List<T> _items;
public IList<T> Items {
    get { return _items.AsReadOnly(); }
}

But I kind of hate this. If another developer is using my class, and my class has a property of type IList<T>, and the whole idea of an interface is: "these are some available properties and methods", why should I throw a NotSupportedException (or whatever the case may be) when he/she tries to do something that, according to the interface, should be completely legal?

I feel like implementing an interface and explicitly defining some of its members is like opening a restaurant and putting some items on the menu -- perhaps in some obscure, easy-to-miss part of the menu, but on the menu nonetheless -- that are simply never available.

It seems there ought to be something like an IArray<T> interface that provides very basic random access by index, but no adding/removing, like the following:

public interface IArray<T> {
    int Length { get; }
    T this[int index] { get; }
}

And then IList<T> could implement ICollection<T> and IArray<T> and add its IndexOf, Insert and RemoveAt methods.

Of course, I could always just write this interface and use it myself, but that doesn't help with all the pre-existing .NET classes that don't implement it. (And yes, I know I could write a wrapper that takes any IList<T> and spits out an IArray<T>, but ... seriously?)

Does anyone have any insight into why the interfaces in System.Collections.Generic were designed this way? Am I missing something? Is there a compelling argument against what I'm saying about my issues with the approach of explicitly defining members of IList<T>?

I'm not trying to sound cocky, as if I know better than the people who designed the .NET classes and interfaces; it just doesn't make sense to me. But I'm ready to acknowledge there's plenty I probably haven't taken into consideration.

解决方案

Design questions are not always black and white.

One side is exact interfaces for each situation, which makes the whole process of actually implementing interfaces a real pain.

The other is few(er) multi-purpose interfaces which aren't always fully supported by the implementor but make many things easier, such as passing instances around which are similar but would not get the same interfaces assigned in the "exact interface" design.

So the BCL designers chose to go the second way. Sometimes I also wish that interfaces were a little less multi-purpose, especially for the collections and with the C#4 interface co-/contravariance features (which cannot be applied to most collection interfaces escept for IEnumerable<> because they contain both co- as well as contravariant parts).

Also, it's a shame that the base classes such as string and the primitive types do not support some interfaces such as ICharStream (for strings, which could be used for regex etc. to allow using other sources than string instances for pattern matching) or IArithmetic for numeric primitives, so that generic math would be possible. But I guess that all frameworks have some weak points.

这篇关于为什么.NET 中没有 IArray(T) 接口?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆