Хотите верьте, хотите нет, я шел вперед и включил этот интерфейс в библиотеку с открытым исходным кодом, которую я запустил, Tao.NET. Я записал сообщение в блоге, объяснив эту библиотеку IArray
интерфейс, который не только решает проблемы я первоначально, повысил в этом вопросе (год назад?!), но также и обеспечивает ковариантный индексируемый интерфейс, что-то, в чем это очень испытывает недостаток (по-моему), в BCL.
Я спросил, почему.NET имеет IList
, который реализует ICollection
и поэтому предоставляет методы для изменения списка (Add
, Remove
, и т.д.), но не предлагает промежуточного интерфейса такой как IArray
обеспечить произвольный доступ индексом без любой модификации списка.
В комментарии к исходному ответу Jon Skeet (в котором он подверг сомнению, как часто можно было бы иметь любую потребность в контракте таким как IArray
), я упомянул что Keys
и Values
свойства SortedList
класс IList
и IList
, соответственно, на который ответил Jon:
Но в этом случае это, как объявляют, IList, и Вы знаете, чтобы просто использовать индексаторы.... Это не чрезвычайно изящно, я соглашаюсь - но это на самом деле не причиняет мне боли.
Это разумно, но я ответил бы путем высказывания, что это не причиняет Вам боли, потому что Вы просто знаете, что не можете сделать этого. Но причина, которую Вы знаете, не состоит в том, что это ясно из кода; это - это, у Вас есть опыт с SortedList
класс.
Visual Studio не собирается давать мне любые предупреждения, если я делаю это:
SortedList mySortedList = new SortedList();
// ...
IList keys = mySortedList.Keys;
keys.Add("newkey");
Это законно, согласно IList
. Но все мы знаем, это собирается вызвать исключение.
Guillaume высказал способное мнение также:
Ну, интерфейсы не прекрасны, но dev может проверить свойство IsReadOnly, прежде чем вызов Добавит/Удалит/Установит...
Снова, это разумно, НО: разве это не кажется тебе немного окольный?
Предположим, что я определил интерфейс следующим образом:
public interface ICanWalkAndRun {
bool IsCapableOfRunning { get; }
void Walk();
void Run();
}
Теперь, предположите также, что я сделал это обычной практикой для реализации этого интерфейса, но только для Walk
метод; во многих случаях я решил бы установить IsCapableOfRunning
кому: false
и бросок a NotSupportedException
на Run
...
Затем у меня мог бы быть некоторый код, который был похож на это:
var walkerRunners = new Dictionary();
// ...
ICanWalkAndRun walkerRunner = walkerRunners["somekey"];
if (walkerRunner.IsCapableOfRunning) {
walkerRunner.Run();
} else {
walkerRunner.Walk();
}
Действительно ли я являюсь сумасшедшим, или действительно ли это - вид нанесения поражения цели названного интерфейса ICanWalkAndRun
?
Я нахожу это очень странным, что в.NET, когда я разрабатываю класс со свойством набора, которое обеспечивает произвольный доступ индексом (или метод, который возвращает индексируемый набор, и т.д.), но не должен или не может быть изменен путем добавления/удаления объектов, и если я хочу "сделать правильную вещь", мудрую ООП и обеспечить интерфейс так, чтобы я мог изменить внутреннюю реализацию, не повреждая API, я должен пойти с IList
.
Стандартный подход, кажется, должен пойти с некоторой реализацией IList
это явно определяет методы Add
, Insert
, и т.д. - обычно путем выполнения чего-то как:
private List _items;
public IList Items {
get { return _items.AsReadOnly(); }
}
Но я вид ненависти это. Если другой разработчик использует мой класс, и мой класс имеет свойство типа IList
, и вся эта мысль об интерфейсе: "это некоторые доступные свойства и методы", почему я должен бросить a NotSupportedException
(или независимо от того, что случай может быть), когда он пытается сделать что-то, что, согласно интерфейсу, должно быть абсолютно законно?
Я испытываю желание реализовывать интерфейс и явно определять некоторых его участников, похож на открытие ресторана и помещение некоторых объектов в меню - возможно, в некоторой неясной, легкой мисс части меню, но в меню, тем не менее - которые никогда не просто доступны.
Кажется, что должно быть что-то как IArray
интерфейс, который обеспечивает очень простой произвольный доступ индексом, но никакое добавление/удаление, как следующее:
public interface IArray {
int Length { get; }
T this[int index] { get; }
}
И затем IList
мог реализовать ICollection
и IArray
и добавьте IndexOf
, Insert
и RemoveAt
методы.
Конечно, я мог всегда просто писать этот интерфейс и использовать его сам, но это не помогает со всеми существующими ранее классами.NET, которые не реализуют его. (И да, я знаю, что мог записать обертку, которая берет любого IList
и выкладывает IArray
, но... серьезно?)
Делает у любого есть любое понимание почему интерфейсы в System.Collections.Generic
были разработаны этот путь? Я пропускаю что-то? Есть ли убедительный аргумент против того, чего я говорю о своих проблемах с подходом явного определения членов IList
?
Я не пытаюсь звучать дерзким, как будто я знаю лучше, чем люди, которые разработали классы.NET и интерфейсы; это просто не имеет смысла мне. Но я готов подтвердить, что существует много, я, вероятно, не учел.
живые () вызовы с такими событиями, как изменение, для меня очень важны. Я хотел этого как-то сейчас.
-121--1002646- Вы можете обойти это, используя Detours , обернув LoadLibrary
.
Оболочка LoadLibrary сможет распознавать попытки загрузки библиотеки DLL и соответствующим образом переписывать путь.
Вопросы дизайна не всегда черно-белые.
Одна сторона - это точные интерфейсы для каждой ситуации, что делает весь процесс фактического внедрения интерфейсов реальной болью.
Другой - это несколько (er) многоцелевых интерфейсов, которые не всегда полностью поддерживаются реализатором, но облегчают многие вещи, такие как передача экземпляров, которые похожи, но не получают те же интерфейсы, назначенные в конструкции «точного интерфейса».
Поэтому BCL дизайнеров выбрал второй путь. Иногда я также желаю, чтобы интерфейсы были немного менее многоцелевыми, особенно для коллекций и с функциями co-/contravariance интерфейса C # 4 (которые не могут быть применены к большинству интерфейсов коллекции escept для IEnumerable < >, потому что они содержат как co-, так и contravariant части).
Также обидно, что базовые классы, такие как последовательности и примитивные типы, не поддерживают некоторые интерфейсы, такие как ICharStream (для последовательностей, которые можно использовать для regex и т.д., чтобы разрешить использование других источников, кроме последовательностей
экземпляров для сопоставления образца) или IArithmetic для числовых примитивов, чтобы была возможна общая математика. Но я думаю, что все рамки имеют некоторые слабые точки.
Самое близкое, которое вы можете получить, это вернуться IENumerable
Ну, интерфейсы не идеальны, но разработчик может проверить свойство ISREADONLY перед вызовом Add / Remove / Set ...