Dot-Net

為什麼 .NET 中沒有 IArray(T) 介面?

  • January 6, 2011

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``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方法;在許多情況下,我會選擇設置IsCapableOfRunningfalse拋出NotSupportedExceptionRun

然後我可能有一些看起來像這樣的程式碼:

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>明確定義方法的實現AddInsert等等 - 通常通過執行以下操作:

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,InsertRemoveAt方法。

當然,我總是可以只編寫這個介面並自己使用它,但這對所有未實現它的預先存在的 .NET 類沒有幫助。(是的,我知道我可以編寫一個包裝器來接收任何內容IList<T>並吐出一個IArray<T>, 但是……認真嗎?)

有沒有人知道為什麼介面System.Collections.Generic是這樣設計的?我錯過了什麼嗎?是否有一個令人信服的論點反對我所說的關於明確定義成員的方法的問題IList<T>

我並不想顯得自大,好像我比設計 .NET 類和介面的人更了解;這對我來說沒有意義。但我已經準備好承認有很多我可能沒有考慮到。

設計問題並不總是非黑即白。

一方面是針對每種情況的精確介面,這使得實際實現介面的整個過程非常痛苦。

另一個是少數(呃)多用途介面,它們並不總是被實現者完全支持,但使許多事情變得更容易,例如傳遞相似但不會獲得“精確介面”設計中分配的相同介面的實例.

所以 BCL 的設計者選擇了第二種方式。有時我也希望介面少一點多用途,特別是對於集合和 C#4 介面協變/逆變特性(不能應用於大多數集合介面,除了 IEnumerable<> 因為它們都包含 co-以及逆變部分)。

此外,令人遺憾的是,諸如字元串和原始類型之類的基類不支持某些介面,例如 ICharStream(用於字元串,可用於正則表達式等,以允許使用除string實例之外的其他源進行模式匹配)或 IArithmetic 用於數字基元,以便通用數學成為可能。但我想所有框架都有一些弱點。

引用自:https://stackoverflow.com/questions/2110440