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