Assertions
От: rameel https://github.com/rsdn/CodeJam
Дата: 30.03.16 16:11
Оценка: +1
Предлагаю добавить в Code/DebugCode следующие методы для единообразия. Всех кейсов наверняка это не покроет, но частое закроет.

NotNullNorWhiteSpace
NotNullNorContainsNull
ValidateRange // или Range|AssertRange? для ArgumentOutOfRangeException
ValidateIndexRange // или IndexRange|AssertIndexRange для для IndexOutOfRangeException


Остальное вроде все уже есть.
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Re: Assertions
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 30.03.16 16:16
Оценка: +1
Здравствуйте, rameel, Вы писали:

R>ValidateRange // или Range|AssertRange? для ArgumentOutOfRangeException

R>ValidateIndexRange // или IndexRange|AssertIndexRange для для IndexOutOfRangeException

С именами не консистентно. Лучше, наверное, RangeValid. Это ж assertion, т.е. утверждение, а не действие.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[2]: Assertions
От: rameel https://github.com/rsdn/CodeJam
Дата: 30.03.16 16:22
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>С именами не консистентно.


Эт точно

AVK>Лучше, наверное, RangeValid. Это ж assertion, т.е. утверждение, а не действие.




Будет RangeValid & IndexRangeValid так?
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Re[3]: Assertions
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 30.03.16 16:37
Оценка:
Здравствуйте, rameel, Вы писали:

R>Будет RangeValid & IndexRangeValid так?


Как вариант. Главное чтобы звучало как утверждение.
И я бы еще переменовал AssertXxx. Например можно в соответствии со стандартным Contract — Requires, Invariant, Exists, Ensures, ForAll.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re: Assertions
От: Sinix  
Дата: 30.03.16 19:07
Оценка:
Здравствуйте, rameel, Вы писали:

R>Предлагаю добавить в Code/DebugCode следующие методы для единообразия. Всех кейсов наверняка это не покроет, но частое закроет.


R>
R>NotNullNorWhiteSpace
R>NotNullNorContainsNull
R>ValidateRange // или Range|AssertRange? для ArgumentOutOfRangeException
R>ValidateIndexRange // или IndexRange|AssertIndexRange для для IndexOutOfRangeException
R>


R>Остальное вроде все уже есть.


Вот тут надо подумать. Нам точно нужно запихивать специфичные ассерты именно в Code?

Дело в том, что Code.* задуман как расширяемая система. Не в смысле плагины и прочая мутотень, а в том смысле, что соблюдая набор простых рекомендаций очень легко добавлять новые наборы ассертов, не нарушая общей картины. Если от этих правил отклоняться, то получится явакошмар из NUnit — десяток перегрузок, из которых половина неудобна, а вторая нафиг не нужна.


Ну, т.е. ассерты для enumerable выглядят как
EnumerableCode.NotEmptyAndNoNulls(values, "...")
и
DebugEnumerableCode.AssertAll(values, v => v > 0); // только InvalidOperation, т.к. тут проблема с состоянием кода, а не с неверно переданными аргументами


ассерты для IO —
IOCode.FileExists("myFile.txt", "...")
и
DebugIOCode.StreamHasContent(values, v => v > 0);


и тыды.
Все эти правила надо оформить в документ, точнее, переписать из внутренней документации без копипасты. Займусь, но когда — не обещаю.
По конкретным предложениям, имена примерные:

1. NotNullNorWhiteSpace — нужен реальный юз-кейз. Я ещё не видел ни одного метода, в котором использовался бы такой ассерт. Без реальных сценариев я код не добавляю, т.к. неизбежно получается сфероконь на выброс. Исключений не видел.
Вот NotNullNorEmpty для строк/enumerable/массивов можно добавить.

2. NotNullNorContainsNull — да, но это уже к продвинутым сценариям, хорошо бы в EnumerableCode запихнуть.

3. ValidateRange — идём от сценария использования, пара:
Code.ArgumentInRange(insertIndex, nameof(insertIndex), 0, Count);

Code.StateInRange(position, nameof(position), -1, Length);


пойдёт. StateInRange не добавлять, если реального сценария нет.

4. ValidateIndexRange
Code.ValidIndex(index, nameof(index));
Code.ValidIndex(index, nameof(index), Count);
Re[4]: Assertions
От: Sinix  
Дата: 30.03.16 19:31
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>Как вариант. Главное чтобы звучало как утверждение.

AVK>И я бы еще переменовал AssertXxx. Например можно в соответствии со стандартным Contract — Requires, Invariant, Exists, Ensures, ForAll.

Я с этого начинал. Не работает, если применять в промышленных масштабах. Можешь поверить, я это уже лет десять делаю
Точнее, так: Requires, Invariant, Exists, Ensures, ForAll можно сделать отдельной группой ассертов, не трогая существующие, но они будут малополезны.


Проблема с ассертами в том, что
1. Должны быть чёткие рекомендации, как оформлять ассерты. Без них получается яваад, когда половину кода нельзя использовать, а вторую половину — не нужно использовать.
Если придерживаться — то у пользователя новые ассерты не вызывают никаких вопросов, он даже про то, что есть какие-то правила оформления и не задумывается.
Рекомендации есть, оформлю.

2. Ассерты используются не в единице мест, а сотнями / тысячами. В больших проектах — легко за десятки тысяч. Соответственно, любой способ использовать неправильно будет использован неправильно, а любой косяк по перфомансу обязательно выстрелит.

В чём проблема с "переменовал AssertXxx":

Все ассерты делятся на
1. Ассерты, покрывающие готовые сценарии. Называются по имени проверяемого сценария (внезапно, ага )
Например, группа ассертов для валидации аргуметров. Обычно бросают ArgumentException/наследников.
Или группа ассертов для валидации состояния. Бросают InvalidOperationException, ObjectDisposed, FileNotExists etc

2. Базовые ассерты для каждой группы. Имя — Assert+Имя группы
Используются, когда в группе нет подходящих. Соответственно, AssertArgument и AssertState.

К базовым обязательно добавляется AssertBusiness для исключений биз-логики, дальше — в зависимости от задач.

Если базовые назвать как-то иначе, то все новые ассерты выбиваются из системы и начинается кто в лес, кто по дрова.
Re[5]: Assertions
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 30.03.16 19:48
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Я с этого начинал. Не работает, если применять в промышленных масштабах. Можешь поверить, я это уже лет десять делаю


Что именно не работает?

S>Точнее, так: Requires, Invariant, Exists, Ensures, ForAll можно сделать отдельной группой ассертов, не трогая существующие, но они будут малополезны.


Я не предлагаю копировать оттуда методы, я предлагаю переименовать AssertArg в ArgRequires.

S>1. Должны быть чёткие рекомендации, как оформлять ассерты. Без них получается яваад, когда половину кода нельзя использовать, а вторую половину — не нужно использовать.


Это к вопросу о Плагин к Решарперу/Розлину
Автор: AndrewVK
Дата: 29.03.16
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[2]: Assertions
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 30.03.16 19:55
Оценка: 2 (1) +1
Здравствуйте, Sinix, Вы писали:

S>Вот тут надо подумать. Нам точно нужно запихивать специфичные ассерты именно в Code?


Диапазоны я бы не назвал специфичными. Оно нечастно бывает нужно, факт, но все таки оно весьма универсально. В ту же копилку проверки чисел на >0 и енумов на существование значения. Разносить их по разным классам не вижу смысла, Code сейчас явно не перегружен.
Вот если тип аргумента за пределами System, то надо в отдельный класс.

S>ассерты для IO -

S>
S>IOCode.FileExists("myFile.txt", "...")
S>и
S>DebugIOCode.StreamHasContent(values, v => v > 0);
S>


IO однозначно в отдельный класс и даже отдельный неймспейс.

S>1. NotNullNorWhiteSpace — нужен реальный юз-кейз. Я ещё не видел ни одного метода, в котором использовался бы такой ассерт.


Да вроде все очевидно, не? Мне такое регулярно попадается.

S>Вот NotNullNorEmpty для строк/enumerable/массивов можно добавить.

S>2. NotNullNorContainsNull — да, но это уже к продвинутым сценариям, хорошо бы в EnumerableCode запихнуть.

Тут спорно. Для enumerable лишняя итерация для проверки на empty — не айс. В 99% случаев лучше проверить в ходе основного энумерирования.
Можно в дебаг, наверное, воткнуть.

S>пойдёт. StateInRange не добавлять, если реального сценария нет.


Ну, если уж озабочиваться проверками инвариантов, то сценарий вполне реален.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[6]: Assertions
От: Sinix  
Дата: 30.03.16 20:07
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>Что именно не работает?

Ассерты называются как каждый автор захочет и получается примерно то, что сейчас творится с ассертами в Nunit: часть obsolete, часть дублирует существующие и без справки в этом болоте не разберёшься


S>>Точнее, так: Requires, Invariant, Exists, Ensures, ForAll можно сделать отдельной группой ассертов, не трогая существующие, но они будут малополезны.

AVK>Я не предлагаю копировать оттуда методы, я предлагаю переименовать AssertArg в ArgRequires.
А есть доводы за? Кроме довода "так сделано в code contracts", который не подходит, т.к. у контрактов немножко другая философия. Одно "нарушение контракта должно убивать процесс" чего стоит.


Против — вот прямо сходу есть два довода.

Во-первых, название метода перестаёт отвечать на вопрос: "какую проблему решает этот метод?"

Во-вторых, нарушается принцип наименьшего удивления.
Сравни сам:
Code.AssertArgument(arg > 0 && arg < 10, nameof(arg), "Some message1");

Code.AssertState(IsInitialized, "Some message2"); // InvalidOperationException

Code.AssertBusiness(CheckSomePolicy(), "Some message3");  // BusinessException

Code.AssertWeb(timeout.TotalSeconds < 15, "Some message4"); // WebException


Для первого сделаем ArgRequires, остальные как обзывать?
AssertState, к примеру, ни разу не Invariant.



S>>1. Должны быть чёткие рекомендации, как оформлять ассерты. Без них получается яваад, когда половину кода нельзя использовать, а вторую половину — не нужно использовать.


AVK>Это к вопросу о Плагин к Решарперу/Розлину
Автор: AndrewVK
Дата: 29.03.16

Угу. Но я боюсь что-либо обещать, т.к. пока у меня и свободного часа в сутки не набирается.
Re[7]: Assertions
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 30.03.16 20:15
Оценка:
Здравствуйте, Sinix, Вы писали:

AVK>>Что именно не работает?

S>Ассерты называются как каждый автор захочет и получается примерно то, что сейчас творится с ассертами в Nunit: часть obsolete, часть дублирует существующие и без справки в этом болоте не разберёшься

Ну вот я и предлагаю придумать правила именования хотя бы примерные.


AVK>>Я не предлагаю копировать оттуда методы, я предлагаю переименовать AssertArg в ArgRequires.

S>А есть доводы за?

Так уж принято — assert методы должны звучать как утверждение, исторически сложилось. У тебя же рядом NotNull именно такой. Не вижу причин отходить от этого правила.

S> Кроме довода "так сделано в code contracts"


Не только. В NUnit тоже самое (за исключением позже добавленногоь бардака, который ты упоминал).

S>AssertState, к примеру, ни разу не Invariant.


Почему? Набор утверждений о состоянии это инвариант и есть.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[8]: Assertions
От: Sinix  
Дата: 30.03.16 20:46
Оценка:
Здравствуйте, AndrewVK, Вы писали:


AVK>Ну вот я и предлагаю придумать правила именования хотя бы примерные.


Угу, сделаю. My bad, не подумал, что народу не хватит того, что добавил.



AVK>Так уж принято — assert методы должны звучать как утверждение, исторически сложилось. У тебя же рядом NotNull именно такой. Не вижу причин отходить от этого правила.

Ну как бы да, но есть случаи, когда это не ассерт в чистом виде, а заготовка для него.

Как раз недавно добавлял Code.DisposedIf()/Code.DisposedIfNull().

Почем так названо:

1. В большинстве реализаций dispose паттерна выставляется флаг disposed или просто обнуляется какое-то из полей.
Двойное отрицание вида
Code.NotDisposed(!disposed, GetType());

// или
Code.NotDisposed(file != null, GetType());
выглядит очень криво.

2. В 99% случаев такие заготовки не используются напрямую, а оборачиваются в хелпер. Хотя бы затем, чтоб не копировать сложную строку форматирования или сложное условие из метода в метод.
Вот так
protected void AssertNotDisposed() => Code.DisposedIf(disposed, this);
оно выглядит гораздо лучше.


S>>AssertState, к примеру, ни разу не Invariant.

AVK>Почему? Набор утверждений о состоянии это инвариант и есть.

Не, инвариант — это условие, которое должно соблюдаться всё время жизни объекта.

А state — это проверка, что мы находимся в состоянии, в котором можно вызывать конкретный метод.

Ну, например, для SqlConnection:
корректная строка соединения — инвариант.
есть открытое соединение — проверка нужна только перед выполнением запросов. Для остальных методов (например, для CreateCommand) она необязательна.
Re[9]: Assertions
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 30.03.16 20:51
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Не, инвариант — это условие, которое должно соблюдаться всё время жизни объекта.


Вовсе нет. Скоуп может быть любым — метод, цикл и т.п. Это в СС оно все время жизни.

In computer science, an invariant is a condition that can be relied upon to be true during execution of a program, or during some portion of it. It is a logical assertion that is held to always be true during a certain phase of execution. For example, a loop invariant is a condition that is true at the beginning and end of every execution of a loop.

Википедия.
Но, вобщем, тут не критично, можно и State. А вот Assert мне чисто эстетически не нравится — выглядит как объемный boilerplate, не несущий смысла. И так понятно, что раз уж мы класс Code написали, то дальше будет assert, нет смысла еще раз напоминать об этом.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[3]: Assertions
От: Sinix  
Дата: 30.03.16 20:57
Оценка:
Здравствуйте, AndrewVK, Вы писали:


S>>Вот тут надо подумать. Нам точно нужно запихивать специфичные ассерты именно в Code?

AVK>Диапазоны я бы не назвал специфичными.
... Вот этот ответ пропустил, сорри.

Ок, значит сделаю завтра
* NotNullNorEmpty для строк/массивов/коллекций, c проверками для enumerable потом определимся.
* NotNullNorWhiteSpace — пусть будет, раз надо
* ArgumentInRange/StateInRange + ValidIndex. Имена фиговые, так что если есть получше — велкам!

Ничего не пропустил?


Не в порядке спора

S>>1. NotNullNorWhiteSpace — нужен реальный юз-кейз. Я ещё не видел ни одного метода, в котором использовался бы такой ассерт.

AVK>Да вроде все очевидно, не? Мне такое регулярно попадается.

А можно пример?
А то я не могу сообразить, где допустима любая непустая строка, если она не из пробелов.
Re[10]: Assertions
От: Sinix  
Дата: 30.03.16 21:00
Оценка:
Здравствуйте, AndrewVK, Вы писали:


AVK>Но, вобщем, тут не критично, можно и State. А вот Assert мне чисто эстетически не нравится — выглядит как объемный boilerplate, не несущий смысла. И так понятно, что раз уж мы класс Code написали, то дальше будет assert, нет смысла еще раз напоминать об этом.


Мне тож не нравится, но способа лучше разделить методы "вот тебе готовая проверка для конкретного сценария" и "не нашёл? Ну вот тебе заготовка, допиливай" я не придумал
Re[4]: Assertions
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 30.03.16 21:06
Оценка: 1 (1) +1
Здравствуйте, Sinix, Вы писали:

S>А можно пример?

S>А то я не могу сообразить, где допустима любая непустая строка, если она не из пробелов.

Всевозможные id'шники, пути, имена.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[3]: Assertions
От: rameel https://github.com/rsdn/CodeJam
Дата: 31.03.16 15:52
Оценка:
Здравствуйте, AndrewVK, Вы писали:

S>>Вот NotNullNorEmpty для строк/enumerable/массивов можно добавить.


Здесь согласен с Андреем, если проверять на Empty для IEnumerable, то равзе что только в дебаг засунуть. В своих проектах такое было сделано только для ICollection<T> и отдельно для массивов

S>>2. NotNullNorContainsNull — да, но это уже к продвинутым сценариям, хорошо бы в EnumerableCode запихнуть.


Здесь тоже, но только для IList<T>.

AVK>Тут спорно. Для enumerable лишняя итерация для проверки на empty — не айс. В 99% случаев лучше проверить в ходе основного энумерирования.


+1

По поводу NotNullNorContainsNull для IList<T>, то в ряде случаев, когда в конструктор передается enumerable, то обычно он материализуется в массив ну или список для более быстрого доступа позже, а как таковое итерирование происходит именно потом, не в конструкторе, и вот тогда может вылететь NRE.

Это вариация на тему инвариантов на вроде ForAll(v => v != null).

S>>пойдёт. StateInRange не добавлять, если реального сценария нет.


AVK>Ну, если уж озабочиваться проверками инвариантов, то сценарий вполне реален.


+1
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Re[2]: Assertions
От: rameel https://github.com/rsdn/CodeJam
Дата: 31.03.16 16:03
Оценка: +1
Здравствуйте, Sinix, Вы писали:

S>Вот тут надо подумать. Нам точно нужно запихивать специфичные ассерты именно в Code?


S>Дело в том, что Code.* задуман как расширяемая система.


Я смотрел на них как на готовый набор предусловий для наиболее частых вариантов использования, которые заменяют всякие if (condition) throw в начале методов и конструкторов. Просто не нравится, когда проверки частых предусловий не консистентны. Вот взять тот же Split:
public static IEnumerable<T[]> Split<T>([NotNull] this IEnumerable<T> source, int size)
{
    Code.NotNull(source, nameof(source));

    // Вот здесь хотелось бы не if & throw, а что-то типа такого как пример:
    // Code.RangeValid(size > 0, nameof(size))
    // с выбросом нужного исключения.
    if (size <= 0)
        throw new ArgumentOutOfRangeException(nameof(size));

    return SplitImpl(source, size);
}
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Re[3]: Assertions
От: Sinix  
Дата: 31.03.16 16:11
Оценка: 6 (1) :)
Здравствуйте, rameel, Вы писали:

R>Я смотрел на них как на готовый набор предусловий для наиболее частых вариантов использования, которые заменяют всякие if (condition) throw в начале методов и конструкторов. Просто не нравится, когда проверки частых предусловий не консистентны. Вот взять тот же Split:


Ну а кто вам виноват, что используете недоделанную вещь?
Если серьёзно — мой косяк, не ожидал, что интерес будет. Рекомендации в процессе, за ними будут дополнения в самом Code Выше был пост с ответом AndrewVK, есть предложения/возражения — велкам!
Re[4]: Assertions
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 31.03.16 18:20
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Если серьёзно — мой косяк, не ожидал, что интерес будет.


Догфудинг пока никто не отменял. И поддержка решарпером тут тоже была бы крайне полезна.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re: Assertions
От: Sinix  
Дата: 02.04.16 17:21
Оценка: 12 (1)
Здравствуйте, rameel, Вы писали:

R>Предлагаю добавить в Code/DebugCode следующие методы для единообразия. Всех кейсов наверняка это не покроет, но частое закроет.


Забирай.

Code.InRange, ValidIndex, ValidIndexPair, ValidIndexAndCount, NotNullNorWhiteSpace
Дебаг-ассерты для всех сгенерены, примеры в тестах.

Перегрузки ValidIndex проверяют index >= 0, 0 <= index < length соответственно

ValidIndexPair: 0 <= from <= to < length

ValidIndexAndCount:
0 <= startIndex < length
+
0 <= count <= length-startIndex;
обычно используется в аналогах Substring/CopyTo etc

Для бросания исключений в switch по аргументу добавлен CodeExceptions.UnexpectedArgumentValue


Не сделаны: ассерты для массивов/коллекций/enumerable. Это в отдельное обсуждение плиз.

Доки в процессе, не всё сразу
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.