Здравствуйте, samius, Вы писали:
S>Например, в одной из бетта версий (не помню конкретно которой) при кастинге массива в IList<T> получался полностью иммутабельный объект, т.е. через сеттер индексатора IList<T> изменить содержимое массива было нельзя. Однако в документации той же беты было упоминание о том, что не стоит в дальнейшем рассчитывать на такое поведение. Причин отказа от этого решения я не знаю.
О, вот это решение кажется наиболее консистентным.
Здравствуйте, _FRED_, Вы писали:
_FR>Если вы открыто высказываете мнение об ошибках проектироващиков, но при этом не в курсе, как проктировалось осуждаемое вами и почему оно именно такое — то не оскоррбляться надо, когда вам рассказывают, как дело было и почему и зачем вышло так,
Я лично сомневаюсь что ты знаешь как проектировалось осуждаемое нами, тебя нет ни в списке авторов дизайна ни в списке авторов книг по оному.
"а мотать на ус, испытывая чувство благодарности за то, что кто-то тратит время, рассказывая вам то, что вы ранее не знали"
Здесь не тот случай, что кто то чего то не знает, а наоборот, знают и более того, имеют свое мнение.
А ты вот объяснений внятных дать не можешь, внятно покритиковать — тоже не можешь.
Вобщем испытывай чувство благодарности когда ктото тратит своё время показывая тебе упущения в ведении дискуссии
Здравствуйте, vitasR, Вы писали:
R>Здравствуйте, Аноним, Вы писали:
А>>думаю, проблема в другом — вы мыслите с точки зрения программиста, а я пользователя
R>"пользователь массива" — это звучит гордо!
Здравствуйте, Qbit86, Вы писали:
Q>Это способ избавиться от комбинаторного взрыва.
Это плохой способ избавиться от комбинаторного взрыва.
Он говорит о том, что имеются расфокусированные абстракции.
Здравствуйте, adontz, Вы писали:
A>И как вы предлагаете это реализовать? IList<> может быть поиницилизирован очень далеко, вообще в другом модуле. С какой стати компилятору запрещать вам вызывать метод?
Запрещать нужно не компилятору, а работодателю... нанимать тех кто проектирует такие интерфейсы.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, adontz, Вы писали:
S>>Я тоже считаю что в интерфейсах коллекций бардак. Другое дело — я привык к нему. Предлагать ничего не буду, не готов прямо сейчас, да и бестолку это. Но уверен, что можно было сделать лучше.
A>Без множественного наследования — нельзя.
Для справки: в дотнете для интерфейсов поддерживается множественное наследование. Ну, а интерфейс нужно было проектировать примерно так:
interface IList<T>
{
члены для чтения данных
}
interface IMutableList<T> : IList<T>
{
члены для изменения данных
}
Заметь, даже множественное наследование не понадобилось. Хотя можно было и его заюзать.
ЗЫ
Ром, странно от тебя слышать такое.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, samius, Вы писали:
S>Впрочем, можно было бы сделать аналог QueryInterface, который бы в динамике решал, возвращать ли или нет запрашиваемую грань.
Это и делать не надо. Есть механизм динамического запроса интефрейсов (динамическое приведение типов). От одного интерфейса всегда можно попросить другой даже если между ними нет никакой связи (пример на шарпе):
interface IList2
{
this[int index] { get; }
}
interface IMutableList2
{
this[int index] { get; set; }
}
class MyLis2 : IMutableList2, IList2 { ... }
...
IList2 lst = new MyLis2();
IMutableList2 mutableLst = (IMutableList2)lst;
...
// или с проверкойvar mutableLst = lst as IMutableList2;
if (mutableLst != null)
...
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, samius, Вы писали:
S>>Впрочем, можно было бы сделать аналог QueryInterface, который бы в динамике решал, возвращать ли или нет запрашиваемую грань.
VD>Это и делать не надо. Есть механизм динамического запроса интефрейсов (динамическое приведение типов). От одного интерфейса всегда можно попросить другой даже если между ними нет никакой связи (пример на шарпе):
Речь шла о том что коллекция меняет свою мутабельность в динамике. Вот я и вспомнил, про IUnknown.QueryInterface. Правда в COM AFAIR были законы по поводу QE, что в рантайме набор поддерживаемых интерфейсов не меняется для объекта.
Здравствуйте, samius, Вы писали:
A>>Смысла в это всё равно нет, потому что я лично сам встречался со случаями когда коллекция ReadOnly/FixedSize иногда. Интерфейсами такое не обозначить.
S>Впрочем, можно было бы сделать аналог QueryInterface, который бы в динамике решал, возвращать ли или нет запрашиваемую грань.
Это фигня какая то Управление набором интерфейсов — вынос мозга.
Автор Design Guidelines пишет, что надо пользоваться например (obj is Array)
Вообще интерфейсы очень полезная штукенция, пустые интерфейсы это например аццки быстрый аналог атрибутов у класса.
Здравствуйте, Ikemefula, Вы писали:
I>Здравствуйте, samius, Вы писали:
A>>>Смысла в это всё равно нет, потому что я лично сам встречался со случаями когда коллекция ReadOnly/FixedSize иногда. Интерфейсами такое не обозначить.
S>>Впрочем, можно было бы сделать аналог QueryInterface, который бы в динамике решал, возвращать ли или нет запрашиваемую грань.
I>Это фигня какая то Управление набором интерфейсов — вынос мозга.
+1
I>Автор Design Guidelines пишет, что надо пользоваться например (obj is Array)
это по сути единственный выход, но он гарантирует решение лишь для массива. А что если мне нужно написать Array2?
I>Вообще интерфейсы очень полезная штукенция, пустые интерфейсы это например аццки быстрый аналог атрибутов у класса.
Да, но в том же Guidlines где-то написано что пустые интерфейсы использовать нежелательно. Что мне собственно не мешало их использовать.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, samius, Вы писали:
S>>Речь шла о том что коллекция меняет свою мутабельность в динамике.
VD>Это где это? здесь
Здравствуйте, VladD2, Вы писали:
VD>Заметь, даже множественное наследование не понадобилось. Хотя можно было и его заюзать.
Я о множестве стандартных реализаций на все случаи жизни. Они, кстати, должны ещё уметь агрегировать друг друга, чтобы MutableList можно было кое-кому выдавать как ReadOnlyList. Экспоненциальный рост количестве классов получается без множественного наследования. Да и с них фигня входит в общем-то, но уже другого рода.
Здравствуйте, adontz, Вы писали:
A>Я о множестве стандартных реализаций на все случаи жизни. Они, кстати, должны ещё уметь агрегировать друг друга, чтобы MutableList можно было кое-кому выдавать как ReadOnlyList. Экспоненциальный рост количестве классов получается без множественного наследования. Да и с них фигня входит в общем-то, но уже другого рода.
Не выдумывай. Никакого экспоненциально роста не будет. Будте грамотный дизайн. А то что есть — это бардак.
В одном ты правл, уже вряд ли кто-то что-то станет менять.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Не выдумывай. Никакого экспоненциально роста не будет. Будте грамотный дизайн. А то что есть — это бардак.
Влад, если бы сам сел и внимательно расписал все классы, то не стал бы спорить. Впрочем с твоим опытом проектирования необходимости в ручке и бумаге быть не должно, так что ты меня попросту удивляешь.
Ладно, я сделаю эту работу за тебя. Для полноты картины и в назидание потомкам.
public interface IEnumerable<T>
{
IEnumerator<T> GetEnumerator();
}
public interface ICollection<T> : IEnumerable<T>
{
bool Contains(T item);
bool CopyTo(T[] array, int arrayIndex);
int Count {get;}
}
// Это ничего, что Clear Вместе с Add/Remove или ещё делить?
// Где обоснованная граница? Бывает же, что Add/Remove можно, а Clear - нельзя.
// А ещё бывает, что можно Add, но нельзя Remove.public interface IMutableCollection<T> : ICollection<T>
{
void Clear();
void Add(T item);
bool Remove(T item);
}
// Опять таки, всегда ли делать доступным IndexOf?
// Метод весьма сомнительный, в колелкции с неуникальными элементами вводит в заблуждение,
// лучше уж IList<int> IndicesOf(T item), но такой метод плох в коллекции с уникальными элементами.public interface IList<T> : ICollection<T>
{
int IndexOf(T item);
T this[int index] { get; }
}
public interface IMutableAddRemoveList<T> : IList<T>, IMutableCollection<T>
{
int Insert(int index, T item);
bool RemoveAt(int index);
}
public interface IMutableModifyList<T> : IList<T>
{
// Это полностью новое свойство, нельзя доопределить сеттер.
T this[int index] { get; set; }
}
////////////////////////////////////////////////////////////////////////////////
// Реализация по умолчанию, как List<T>public class MutableAddRemoveList<T> : IMutableAddRemoveList<T>
{
...
}
// Реализация по умолчанию, как List<T>public class MutableModifyList<T> : IMutableModifyList<T>
{
...
}
// Реализация по умолчанию, как List<T>
// Copy+Paste их двух предыдущих классов.public class MutableList<T> : IMutableAddRemoveList<T>, IMutableModifyList<T>
{
...
}
Уже три класса вместо одного, и двойное повторение кода. В два раза больше кода, полученного обычной копипастой. Сомнительный признак хорошей архитектуры. И заметь, есть ещё куча возможностей улучшить архитектуру разбив интерфейсы на более мелкие! Теперь добавим сюда observable collections и разные другие фенечки и получим ещё одну волну увеличения количества классов и копий кода. В Си++ эта проблема решалась: MutableList<T> унаследовался бы от MutableAddRemoveList<T> и MutableModifyList<T>. В .Net эта проблема не решается непосредственно.
Кроме того, что делать с ситуациями, когда доступно операции величина не постоянная? Вот скажем у меня коллекция связана с каким-то хранилищем (БД или что-то ещё) и менять её (писать в хранилище) можно только наложив блокировку. То есть в каких-то случаях (когда блокировки нет) IsReadOnly вернёт true, а Add(T) выбросит InvalidOperationException, а в каких-то других случаях IsReadOnly вернёт false и Add(T) отработает штатно. Как такие сценарии сочетаются с идеей fine grained interfaces?
То есть, если резюмировать, то что ты предлагаешь вызывает бурный рост количества сущностей, кучу копирования кода при реализации коллекций и лишь частичное покрытие возможных сценариев.
Здравствуйте, adontz, Вы писали:
A>Здравствуйте, VladD2, Вы писали:
VD>>Не выдумывай. Никакого экспоненциально роста не будет. Будте грамотный дизайн. А то что есть — это бардак.
A>Влад, если бы сам сел и внимательно расписал все классы, то не стал бы спорить. Впрочем с твоим опытом проектирования необходимости в ручке и бумаге быть не должно, так что ты меня попросту удивляешь.
A>Ладно, я сделаю эту работу за тебя. Для полноты картины и в назидание потомкам.
A>
A>public interface IMutableAddRemoveList<T> : IList<T>, IMutableCollection<T>
A>{
A> int Insert(int index, T item);
A> bool RemoveAt(int index);
A>}
A>public interface IMutableModifyList<T> : IList<T>
A>{
A> // Это полностью новое свойство, нельзя доопределить сеттер.
A> T this[int index] { get; set; }
A>}
A>////////////////////////////////////////////////////////////////////////////////
A>// Реализация по умолчанию, как List<T>
A>public class MutableAddRemoveList<T> : IMutableAddRemoveList<T>
A>{
A>...
A>}
A>// Реализация по умолчанию, как List<T>
A>public class MutableModifyList<T> : IMutableModifyList<T>
A>{
A>...
A>}
A>// Реализация по умолчанию, как List<T>
A>// Copy+Paste их двух предыдущих классов.
A>public class MutableList<T> : IMutableAddRemoveList<T>, IMutableModifyList<T>
A>{
A>...
A>}
A>
Это чушь какая-то. Зачем все эти классы? Если у нас скажем SCG.List<T> поддерживает индексированный доступ и изменение, то реализуем в нем IMutableList<T>. Если это скажем однонаправленный связанный список являющийся неизменяемой структурой данных, то реализуем в нем только ICollection<T> и т.д.
A>Уже три класса вместо одного, и двойное повторение кода. В два раза больше кода, полученного обычной копипастой.
Дык ты намеренно глупость же сделал.
A>СВ .Net эта проблема не решается непосредственно.
Да нет никакой проблем на самом то деле. Ты ее сам из пальца высосал.
В прочем, на самом деле нет особой нужды в Mutable-интерфейсах. Вот скажи, ты сам то часто на практике добавлял элементы через IList<T> или ICollection<T>? Я — ни разу. Когда коллекция заполняется, то обычно работа идет с конкретными типами. А вот кода мы ее передам в некий обработчик, то тут хорошо бы иметь дело с абстракцией. Но при этом обычно речь идет о чтении данных, а не об их изменении. По сему на практике в большинстве случаев достаточно IEnumerable<T>. Вот по этому то наличие всех этих лишних методов на практике особых проблем и не вызывает. Но это никак не доказывает необходимость наличия этих методов.
A>Кроме того, что делать с ситуациями, когда доступно операции величина не постоянная? Вот скажем у меня коллекция связана с каким-то хранилищем (БД или что-то ещё) и менять её (писать в хранилище) можно только наложив блокировку. То есть в каких-то случаях (когда блокировки нет) IsReadOnly вернёт true, а Add(T) выбросит InvalidOperationException, а в каких-то других случаях IsReadOnly вернёт false и Add(T) отработает штатно. Как такие сценарии сочетаются с идеей fine grained interfaces?
Это какая-то выдуманная фигня. Что ее разбирать то?
A>То есть, если резюмировать, то что ты предлагаешь вызывает бурный рост количества сущностей, кучу копирования кода при реализации коллекций и лишь частичное покрытие возможных сценариев.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.