Почему в .NET нет интерфейса IArray (T)?
Верьте или нет, я пошел вперед и включил этот интерфейс вбиблиотека с открытым исходным кодом, которую я начал, Tao.NET, янаписал сообщение в блоге объясняя эту библиотекуIArray<T>
интерфейс, который не только решает проблемы, которые я первоначально поднял в этом вопросе (год назад ?!), но также обеспечиваетковариантный индексированный интерфейсчто-то очень не хватает (по моему мнению) в BCL.
Я спросил, почему .NET имеетIList<T>
, который реализуетICollection<T>
и, следовательно, предоставляет методы для изменения списка (Add
, Remove
и т. д.), но не предлагает никакого промежуточного интерфейса, такого какIArray<T>
обеспечить произвольный доступ по индексу без какой-либо модификации списка.
В комментарии к первоначальному ответу Джона Скита (в котором он задал вопрос, как часто нужно иметь контракт, такой как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>
, Но мы все знаем, что это вызовет исключение.
Гийом также сделал правильное замечание:
Ну, интерфейсы не идеальны, но разработчик может проверить свойство IsReadOnly перед вызовом Add / Remove / Set ...
Опять же, это разумно,НО: это не кажется вам немного окольным?
Предположим, я определил интерфейс следующим образом:
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, когда я разрабатываю класс со свойством коллекции, которое обеспечивает произвольный доступ по индексу (или метод, который возвращает индексированную коллекцию и т. Д.),но не должен или не может быть изменен путем добавления / удаления элементови если я хочу «сделать правильные вещи» в ООП и предоставить интерфейс, чтобы я мог изменить внутреннюю реализацию, не нарушая API, я должен идти сIList<T>
.
Кажется, что стандартный подход заключается в реализацииIList<T>
это явно определяет методыAdd
, Insert
и т. д. - как правило, делая что-то вроде:
private List<T> _items;
public IList<T> Items {
get { return _items.AsReadOnly(); }
}
Но я вроде как ненавижу это. Если другой разработчик использует мой класс, и у моего класса есть свойство типа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; это просто не имеет смысламне, Но я готов признать, что есть много вещей, которые я, вероятно, не учел.