Здесь дело не в сложности сочетания, а в том, что запоминание этой комбинации пока не дает ощутимых преимуществ лично для меня, чтобы ее запомнить.
Я, кстати, не спорю, что это очень полезная фича, но как я написал во введении, я привел не просто абстрактный набор фич решарпера (их любой может посмотреть в хелпе), это те возможности, которыми лично я пользуюсь. А комменты, вроде этого позволяют дополнить приведенный список фич, другими, не менее полезными.
Закладки — ОК. А вот синхронизация положения файла в солюшн эксплорере — не уверен. Мне не настолько часто нужно синхронизировать положение текущего файла, чтобы смириться с постоянными изменениями в UI, которые происходят при навигации.
После вчерашнего обновления хабра у меня вообще проблемы некоторые были: кнопка создания поста появилась только с третьего раза, да и кнопки хабракат до сих пор нет. Влепил тек ката по памяти, но промазал.
Для не UI потока это точно так же полезно, поскольку код становится проще и понятней. Попробуйте переписать любым другим образом код, который будет содержать 3-4 вызова await. Там такая каша получится, будь здоров.
Еще один плюс await-ов в том, что они делают один лексический скоуп, что позволяет пользоваться такими вещами, как using, try/finally и т.д.
Т.е. это не просто более красивый код обработки исключений, эти штуки дают более читабельный код, который можно спокойно читать без боязни заработать непоправимые заболевания от вывиха извилин.
public int Count
{
get
{
// We should add some hints to static checker to eliminate a warning
// Contract.Ensures(Contract.Result() == _backingList.Count);
return _backingList.Count;
}
}
Закомментируйте строку с Contract.Ensures, тогда предупреждение появится, что статик чекер не может гарантировать удовлетворение одного из постусловий.
Да, согласен. Можно юзать что-то типа F#-а, там все референс-типы non-nullable. А из ОО языков, кажется только Eiffel поддерживает non-nullability на уровне языка.
В моем энтерпрайзе мне постоянно приходится думать над следующим: я вижу метод, принимающий некоторый объект. Обычно, подсовывать null туда нельзя, но в редких случаях, и если очень хочется то можно. Т.е. чтобы понять, что мне нужно сделать, мне нужно читать код, а там можем быть полотно спагетти-кода на пару экранов.
Тоже самое касается и постусловий. Если класс, у которого есть коллекция в виде какого-то свойства. В большинство людей используют пустую коллекцию, и никогда не возвращают null, но это далеко не всегда. Постусловие в этом плане четко скажет, чего ожидать от этого свойства (или возвращающего значения метода).
Да, в большинстве случаев более или менее сложные предусловия и постусловия живут в библиотеках, но некоторые бизнес-правила тоже можно формализовать. В целом так: исключения типа ArgumentNullException, InvalidArgumentEception, ArgumentOutOfRangeExcpetion все заменяются на соответствующие предусловия (ведь все эти исключения, на самом деле выбрасываются при нарушении предусловий). С постусловиями и инвариантами сложнее, поскольку они внутренние по отношению к вызывающему коду.
В целом, рассмотрение взаимоотношений классов через призму контрактов позволяет мне понять, сферы ответственности обоих классов, и кто кому что должен. Опять таки, если поднимутся вопросы с некоторыми принципами, типа Liskov Substitution Principle, то контракты здесь очень ок.
Ну и очень ОК использование контрактов для интерфейсов, понять семантику которых только исходя из документации весьма сложно.
Вот оригинальное (на всякий случай) определение LSP:
If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T
Более того, вчера уже перед публикацией этой статьи мы обсуждали с коллегой именно мой «грязный хак» с рассмотрением коллекции, вместо списка. Коллега предположил, что список содержит более строгое постусловие вида: count = old cound + 1, а коллекция содержит более слабое постусловие.
После этого, мы открыли сборку с установленными контрактами и убедились, что постусловие обоих этих интерфейсов для метода Add одинаковы (хотя постусловие наследника спокойно может быть сильнее).
Если бы у списка было бы более строгое постусловия, этой статьи бы не было.
Я говорю лишь о том, что ребята, которые спорили о DoubleList спорили бы точно также и об DoubleCollection, поскольку с точки зрения именно метода Add семантика у обоих этих интерфейсов одинаковая.
Да, список — это более конкретный тип, но с точки зрения добавления элементов семантика их обоих — одинаковая.
З.Ы. +100 500 на счет того, что родителя нельзя заменять на потомка *в общем случае*.
Сори, не правильно понял, но и Вы, видимо тоже (поскольку мы написали одно и тоже): «список — это коллекция с дополнительными условиями» (вы), т.е. «коллекция — это более базовое понятие» (я).
Но, к сожалению, если посмотреть на реальные постусловия метода Add коллекции и списка, то их постусловия являются одинаковыми (у обоих оно Contract.Ensures(Count >= Contract.OldValue(Count);
Можете поставить контракты, чтобы в этом убедиться.
Ну сразу, любой базовый класс или интерфейс по определению является более *базовым* понятием, а наследник — это более узкое понятие. Так что коллекция — это более базовое понятие, и поэтому замена листа на коллекцию является корректным.
Почему использование коллекции вместо списка — это софизм? Если вы откроете документацию к интерфейсу IList of T или классу List of T, то увидите, что метод Add берется из интерфейса ICollection of T.
Главный вопрос из определения LSP: что такое «поведение P не изменится»? Каким образом вы определяете, что оно изменилось и почему кто-то рассчитывает на то, что метод Add обязательно должен добавлять элемент?
Дык весь смысл этой заметки как раз и состоит в ответе на вопрос: а вправе ли мы ожидать от классов реализующих интерфейс ICollection of T определенной семантики или нет. И под конкретной семантикой в данном случае подразумевается: должен ли метод Add обязательно добавлять *еще один элемент* или нет и должен ли быть это только один элемент.
Т.е. по сути, все это попытка найти постусловие метода Add и доказать, что постусловие вида: NewCount = OldCount + 1 не может применяться для этого метода.
Я, кстати, не спорю, что это очень полезная фича, но как я написал во введении, я привел не просто абстрактный набор фич решарпера (их любой может посмотреть в хелпе), это те возможности, которыми лично я пользуюсь. А комменты, вроде этого позволяют дополнить приведенный список фич, другими, не менее полезными.
После вчерашнего обновления хабра у меня вообще проблемы некоторые были: кнопка создания поста появилась только с третьего раза, да и кнопки хабракат до сих пор нет. Влепил тек ката по памяти, но промазал.
Еще один плюс await-ов в том, что они делают один лексический скоуп, что позволяет пользоваться такими вещами, как using, try/finally и т.д.
Т.е. это не просто более красивый код обработки исключений, эти штуки дают более читабельный код, который можно спокойно читать без боязни заработать непоправимые заболевания от вывиха извилин.
public int Count
{
get
{
// We should add some hints to static checker to eliminate a warning
// Contract.Ensures(Contract.Result() == _backingList.Count);
return _backingList.Count;
}
}
Закомментируйте строку с Contract.Ensures, тогда предупреждение появится, что статик чекер не может гарантировать удовлетворение одного из постусловий.
Тоже самое касается и постусловий. Если класс, у которого есть коллекция в виде какого-то свойства. В большинство людей используют пустую коллекцию, и никогда не возвращают null, но это далеко не всегда. Постусловие в этом плане четко скажет, чего ожидать от этого свойства (или возвращающего значения метода).
Да, в большинстве случаев более или менее сложные предусловия и постусловия живут в библиотеках, но некоторые бизнес-правила тоже можно формализовать. В целом так: исключения типа ArgumentNullException, InvalidArgumentEception, ArgumentOutOfRangeExcpetion все заменяются на соответствующие предусловия (ведь все эти исключения, на самом деле выбрасываются при нарушении предусловий). С постусловиями и инвариантами сложнее, поскольку они внутренние по отношению к вызывающему коду.
В целом, рассмотрение взаимоотношений классов через призму контрактов позволяет мне понять, сферы ответственности обоих классов, и кто кому что должен. Опять таки, если поднимутся вопросы с некоторыми принципами, типа Liskov Substitution Principle, то контракты здесь очень ок.
Ну и очень ОК использование контрактов для интерфейсов, понять семантику которых только исходя из документации весьма сложно.
void AddItemToCollection(ICollection c, string s)
{
// здесь мы видим именно метод ICollection.Add, который не возвращает булевого параметра
}
void Foo()
{
var ss = new SortedSet();
// Здесь мы спокойно приводим к интерфейсу и используем SortedSet полиморфно
AddItemToCollection(ss, "hello");
}
If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T
Пока не вижу связи с интерфейсами COM-компонента.
После этого, мы открыли сборку с установленными контрактами и убедились, что постусловие обоих этих интерфейсов для метода Add одинаковы (хотя постусловие наследника спокойно может быть сильнее).
Если бы у списка было бы более строгое постусловия, этой статьи бы не было.
Да, список — это более конкретный тип, но с точки зрения добавления элементов семантика их обоих — одинаковая.
З.Ы. +100 500 на счет того, что родителя нельзя заменять на потомка *в общем случае*.
Но, к сожалению, если посмотреть на реальные постусловия метода Add коллекции и списка, то их постусловия являются одинаковыми (у обоих оно
Contract.Ensures(Count >= Contract.OldValue(Count);
Можете поставить контракты, чтобы в этом убедиться.
Главный вопрос из определения LSP: что такое «поведение P не изменится»? Каким образом вы определяете, что оно изменилось и почему кто-то рассчитывает на то, что метод Add обязательно должен добавлять элемент?
{code}
Contract.Ensures(Count >= Contract.OldValue);
{code}
Но да, в результате Count будет вызвано при включенных постусловиях.
Дык весь смысл этой заметки как раз и состоит в ответе на вопрос: а вправе ли мы ожидать от классов реализующих интерфейс ICollection of T определенной семантики или нет. И под конкретной семантикой в данном случае подразумевается: должен ли метод Add обязательно добавлять *еще один элемент* или нет и должен ли быть это только один элемент.
Т.е. по сути, все это попытка найти постусловие метода Add и доказать, что постусловие вида: NewCount = OldCount + 1 не может применяться для этого метода.