Подтвердить что ты не робот

Почему в .NET нет интерфейса IArray (T)?

Обновление 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; это просто не имеет смысла для меня. Но я готов признать, что, вероятно, я не принял во внимание.

4b9b3361

Ответ 1

Вопросы дизайна не всегда черно-белые.

Одна сторона - это точные интерфейсы для каждой ситуации, что делает весь процесс фактического внедрения интерфейсов реальной болью.

Другим является несколько (эл) многоцелевых интерфейсов, которые не всегда полностью поддерживаются разработчиком, но облегчают многие вещи, такие как передача экземпляров, вокруг которых похожи, но не получат одинаковые интерфейсы, назначенные в "точной интерфейса".

Поэтому дизайнеры BCL решили пойти вторым путем. Иногда мне также хотелось бы, чтобы интерфейсы были немного менее универсальными, особенно для коллекций и с функциями интерфейса/контравариантности интерфейса С# 4 (которые не могут применяться к большинству интерфейсов обмена, за исключением IEnumerable < > , поскольку они содержат как совлокальные, а также контравариантные части).

Кроме того, стыдно, что базовые классы, такие как строка и примитивные типы, не поддерживают некоторые интерфейсы, такие как ICharStream (для строк, которые могут использоваться для регулярного выражения и т.д., чтобы разрешить использование других источников, чем экземпляры string для сопоставление шаблонов) или IArithmetic для числовых примитивов, так что общая математика была бы возможна. Но я думаю, что у всех фреймворков есть слабые места.

Ответ 2

Ну, интерфейсы не идеальны, но разработчик может проверить свойство IsReadOnly до вызова Add/Remove/Set...

Ответ 3

Самое близкое, что вы можете получить, - это вернуть IEnumerable <T> , а затем клиент (вызывающий вызов a.k.a.) может сам вызвать .ToArray().