Обновление 2011-Янв-06:
Верьте или нет, я пошел дальше и включил этот интерфейс в открытую библиотеку с открытым исходным кодом, Tao.NET . Я написал сообщение в блоге , объясняющее интерфейс IArray<T>
этой библиотеки, который не только решает проблемы, которые я первоначально поднял в этом вопросе (год назад ?!), но также предоставляет ковариантный индексированный интерфейс что-то очень не хватает (по моему мнению) в BCL.
Вопрос (вкратце):
Я спросил, почему .NET имеет IList<T>
, который реализует ICollection<T>
и, следовательно, предоставляет методы для изменения списка (Add
, Remove
и т. Д.), Но не предлагает никакого промежуточного интерфейса, такого как IArray<T>
для обеспечения произвольного доступа по индексу без изменения списка.
РЕДАКТИРОВАТЬ 2010-Jan-21 14:22 EST:
В комментарии к первоначальному ответу Джона Скита (в котором он спросил, как часто нужно иметь контракт, такой как 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
собственность перед звонком
Добавить / Удалить / 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; это просто не имеет смысла для меня . Но я готов признать, что есть много вещей, которые я, вероятно, не учел.