Pull to refresh
235
0
Сергей Тепляков @SergeyT

Пользователь

Send message
Например, нужно закоммитить изменения, добавить новый файл рядом etc.
Здесь дело не в сложности сочетания, а в том, что запоминание этой комбинации пока не дает ощутимых преимуществ лично для меня, чтобы ее запомнить.

Я, кстати, не спорю, что это очень полезная фича, но как я написал во введении, я привел не просто абстрактный набор фич решарпера (их любой может посмотреть в хелпе), это те возможности, которыми лично я пользуюсь. А комменты, вроде этого позволяют дополнить приведенный список фич, другими, не менее полезными.
Закладки — ОК. А вот синхронизация положения файла в солюшн эксплорере — не уверен. Мне не настолько часто нужно синхронизировать положение текущего файла, чтобы смириться с постоянными изменениями в 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, то контракты здесь очень ок.

Ну и очень ОК использование контрактов для интерфейсов, понять семантику которых только исходя из документации весьма сложно.
Явная реализация интерфейса не запрещает использовать класс через интерфейс:

void AddItemToCollection(ICollection c, string s)
{
// здесь мы видим именно метод ICollection.Add, который не возвращает булевого параметра
}

void Foo()
{
var ss = new SortedSet();

// Здесь мы спокойно приводим к интерфейсу и используем SortedSet полиморфно
AddItemToCollection(ss, "hello");
}
Вот оригинальное (на всякий случай) определение 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

Пока не вижу связи с интерфейсами COM-компонента.
А как связана бедная Барбара с COM, если принцип замещения появился в 1987, а COM в 1993?
Более того, вчера уже перед публикацией этой статьи мы обсуждали с коллегой именно мой «грязный хак» с рассмотрением коллекции, вместо списка. Коллега предположил, что список содержит более строгое постусловие вида: 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 обязательно должен добавлять элемент?
Сейчас постусловие метода Add ICollection ослабили, теперь там постусловие такое:
{code}
Contract.Ensures(Count >= Contract.OldValue);
{code}

Но да, в результате Count будет вызвано при включенных постусловиях.
Да, HashSet — это часть BCL.

Дык весь смысл этой заметки как раз и состоит в ответе на вопрос: а вправе ли мы ожидать от классов реализующих интерфейс ICollection of T определенной семантики или нет. И под конкретной семантикой в данном случае подразумевается: должен ли метод Add обязательно добавлять *еще один элемент* или нет и должен ли быть это только один элемент.

Т.е. по сути, все это попытка найти постусловие метода Add и доказать, что постусловие вида: NewCount = OldCount + 1 не может применяться для этого метода.

Information

Rating
Does not participate
Location
Washington, США
Registered
Activity