Deutsch
Germany.ruФорумы → Архив Досок→ Программирование

C# - где используется класс Debug?

846  1 2 все
alex445 коренной житель27.11.22 21:19
27.11.22 21:19 

https://learn.microsoft.com/en-us/dotnet/api/system.diagno...

https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.debug.assert?view=net-7.0#system-diagnostics-debug-assert(system-boolean)


Почитал, почитал... Встречаю иногда в проектах всякие Debug.Assert - похоже на юнит-тесты по названию методов. Только не понимаю смысла этой штуки. Максимум что использовал - "родственника" этого класса Debugger и его метод Break - https://learn.microsoft.com/en-us/dotnet/api/system.diagno... Но лишь в качестве замены точки останова.


Вобщем, это всё используется в каких-то продвинутых системах дебага, где всякая статистика собирается, что-то куда-то отсылается и прочее? Когда программер просто дебажит лишь у себя на машине по Ф5 в простейших сценариях, то достаточно обычных точек останова с настройками?

#1 
Программист коренной житель28.11.22 08:36
NEW 28.11.22 08:36 
в ответ alex445 27.11.22 21:19

Все зависит от юз-кейсов.


Debug.Assert удобно использовать для того, чтобы поймать какую-то нестандартную ситуацию.

Самый простой пример - если у тебя есть входной параметр, который должен быть не null, то можно конечно сделать так:

public void DoSomething (IFoo foo)
{
   if (foo != null)
   {
      ...
   }
   else
   {
      throw new SomeException ("Foo can't be null");
   }
}


Это вполне понятное решение.

А можно заменить всю эту конструкцию на Debug.Assert. Тогда в Debug-моде будет брошено исключение и исполнение остановится на этой строке (как если бы там была точка останова с условием), так что у разработчика будет возможность отладить код. При этом в релиз сборке ничего не произойдет и, очевидно, при исполнении кода будет брошено NullReferenceException.


Кроме этого, в Debug есть вывод отладочной ипформации в окно. Что тоже очень удобно. Я например часто использую эту фишку для перенаправки вывода в юнит-тестах. Т.е. делаю свой класс, который выводит ошибки в окошко, подменяют логгер и получается, что я могу видеть все логи во время отладки юнит-тестов. Очень удобно :)

#2 
MrSanders коренной житель28.11.22 10:03
NEW 28.11.22 10:03 
в ответ Программист 28.11.22 08:36

Не про дебаг, но по больному.

Вопрос только в том, где же оно бросится. Не 5-ю ли вызовами позже в строчке, в которой 10 причин для NRE.

Поэтому не "можно конечно сделать так" а "нужно делать так в начале метода"

 if (foo == null)
   {
      throw new SomeException ("Foo can't be null"); // ArgumentException, ArgumentOutOfRangeException
   }

Кстати, а в шарпе с его любовью к украшательствам ещё не сделали какую-нибудь перделку, которая автоматически бросает эксепшен если параметр null?
Типа foo! в декларации метода? Или "покупайте решарпер и радуйтесь [NotNull]" (была ж такая аннотация вроде)

#3 
Программист коренной житель28.11.22 10:27
NEW 28.11.22 10:27 
в ответ MrSanders 28.11.22 10:03
Вопрос только в том, где же оно бросится. Не 5-ю ли вызовами позже в строчке, в которой 10 причин для NRE.

Бросится там, где будет использовано :) А как оно там используется - это уже другой вопрос ;)


Поэтому не "можно конечно сделать так" а "нужно делать так в начале метода"

Это филосовский вопрос. Если у тебя в коде 10 причин для NRE, то делать 10 проверок - это зачастую никому не нужный код. Проверки должны иметь смысл. Зачем писать код, который никогда не будет исполнен? Это будут лишние строки кода, да еще и лишняя документация.

Крайности почти всегда плохи :)


#4 
MrSanders коренной житель28.11.22 10:44
NEW 28.11.22 10:44 
в ответ Программист 28.11.22 10:27

Философский в какой-то мере. Но вроде бы о том, что в публичных методах НАДО проверять все параметры, которые не могут быть null-ем, перестали спорить уже лет 10 назад. Да, надо.

Если у тебя в коде 10 причин для NRE, то делать 10 проверок - это зачастую никому не нужный код.

Никому это сильно. Я бы сказал что это всегда всем, сопровождающим проект, жизненно необходимый код. Искать причины NRE одно из самых увлекательных занятий.

#5 
alex445 коренной житель28.11.22 11:48
NEW 28.11.22 11:48 
в ответ Программист 28.11.22 10:27
Если у тебя в коде 10 причин для NRE, то делать 10 проверок - это зачастую никому не нужный код. Проверки должны иметь смысл. Зачем писать код, который никогда не будет исполнен? Это будут лишние строки кода, да еще и лишняя документация.

Это всё равно что сказать "юнит-тесты должны иметь смысл". Т.е. не 100% покрытие. И даже не 90.

#6 
alex445 коренной житель28.11.22 11:49
NEW 28.11.22 11:49 
в ответ Программист 28.11.22 08:36
А можно заменить всю эту конструкцию на Debug.Assert. Тогда в Debug-моде будет брошено исключение и исполнение остановится на этой строке (как если бы там была точка останова с условием), так что у разработчика будет возможность отладить код.

Так это можно сделать и без Debug.Assert - просто бросить исключение в дебаг моде и отлаживать там. Или имеется ввиду, что код запущен не в среде разработки?

#7 
alex445 коренной житель28.11.22 11:55
NEW 28.11.22 11:55 
в ответ MrSanders 28.11.22 10:03, Последний раз изменено 28.11.22 11:58 (alex445)
Кстати, а в шарпе с его любовью к украшательствам ещё не сделали какую-нибудь перделку, которая автоматически бросает эксепшен если параметр null?
Типа foo! в декларации метода? Или "покупайте решарпер и радуйтесь [NotNull]" (была ж такая аннотация вроде)

Про старый NotNull есть такая инфа. Но щас МС ввела вот такие штуки: 1, 2.

Ну и с версии .NET Core 3.0 этот атрибут снова присутствует.


Решарпер, говорят, жрёт оперативу как не в себя (до перехода на 64-битную Студию это была проблема) и, грубо говоря, понижает грейд твоего процессора примерно на 1 (т.е. Core i7 начинает ворочаться как i5). А его фичи постепенно переходят на "Студию из коробки".

#8 
Программист коренной житель28.11.22 12:05
NEW 28.11.22 12:05 
в ответ MrSanders 28.11.22 10:44
Философский в какой-то мере. Но вроде бы о том, что в публичных методах НАДО проверять все параметры, которые не могут быть null-ем, перестали спорить уже лет 10 назад. Да, надо.

Ну вот у нас как раз такой случай, когда проверять все публичные методы не надо :)

У нас так, есть фабрика объектов, которая конфигурируется в xml. И многие (но не все), объекты (контроллеры, фабрики итд) создаются в рантайме. Все это работает по принципу депенденси инжектора, т.е. если какой-то объект создается фабрикой и при этом имеет какие-то параметры, то параменты эти тоже идут из фабрики. Короче говоря, такой самописный Ninject (было написано задолго до появления котовых контейнеров).

Короче, те объекты, которые создаются фабрикой никто никогда не проверяет на null, просто пишут Debug.Assert и этого достаточно. Используется опять же в основном в тестах, просто чтобы не забыть мокнуть какой-нибудь объект.


Искать причины NRE одно из самых увлекательных занятий.

Ну не знаю, как по мне, это одна из самых простых задач :) Если конечно в null не скидывается где-то в соседних потоках :D А так, брейк-поинты с условием - и останавливаешься там где надо. Ну и Debug.Assert очень помогает :)

#9 
Программист коренной житель28.11.22 12:06
NEW 28.11.22 12:06 
в ответ alex445 28.11.22 11:48
Это всё равно что сказать "юнит-тесты должны иметь смысл". Т.е. не 100% покрытие. И даже не 90.

Так и есть. Инит-тесты должны иметь смысл. Тот, кто гонятся за покрытием тратит время и деньги в пустую :)

#10 
Программист коренной житель28.11.22 12:08
NEW 28.11.22 12:08 
в ответ alex445 28.11.22 11:49
Так это можно сделать и без Debug.Assert - просто бросить исключение в дебаг моде и отлаживать там. Или имеется ввиду, что код запущен не в среде разработки?

Именно это и делает Debug.Assert :) Debug.Assert работает только в дебаг-сборке. А релиз-сборке Debug.Assert игнорируется.

#11 
alex445 коренной житель28.11.22 13:55
NEW 28.11.22 13:55 
в ответ Программист 28.11.22 12:06, Последний раз изменено 28.11.22 13:56 (alex445)
Это всё равно что сказать "юнит-тесты должны иметь смысл". Т.е. не 100% покрытие. И даже не 90.
Так и есть. Инит-тесты должны иметь смысл. Тот, кто гонятся за покрытием тратит время и деньги в пустую :)

Разве TDD не предполагает 100% покрытия? Там же кода вообще нет, пока тесты не написаны. А значит по определению код покрывается 100%.

#12 
alex445 коренной житель28.11.22 13:58
NEW 28.11.22 13:58 
в ответ Программист 28.11.22 12:08, Последний раз изменено 28.11.22 13:59 (alex445)
Так это можно сделать и без Debug.Assert - просто бросить исключение в дебаг моде и отлаживать там. Или имеется ввиду, что код запущен не в среде разработки?
Именно это и делает Debug.Assert :) Debug.Assert работает только в дебаг-сборке. А релиз-сборке Debug.Assert игнорируется.

Я имею ввиду, зачем бросать исключение с помощью специального метода, когда можно бросить через throw? Я и предположил, что может класс Debug используется в разных сложных случаях тестирования, с записью логов тестирования и прочего, для чего должна быть развёрнута соответствующая инфраструктура (где-то хранить эти логи, инструменты для чтения логовых портянок, поиска по ним и т.д.), а не просто "запустил - потыкал - сойдёт"?

#13 
alex445 коренной житель28.11.22 14:02
NEW 28.11.22 14:02 
в ответ Программист 28.11.22 12:05, Последний раз изменено 28.11.22 14:12 (alex445)
Искать причины NRE одно из самых увлекательных занятий.
Ну не знаю, как по мне, это одна из самых простых задач :) Если конечно в null не скидывается где-то в соседних потоках

Странно. В той же Студии из покон веков есть инструмент для слежения за состоянием переменных (команда "Add Watch"). Нельзя к нему, чтоли, приделать условие, чтобы код останавливался в том месте, где значение переменной обналливается?


...А вот и оно. Называется Data Breakpoint. Можно юзать, когда нет возможности сделать проверку в сеттере (сеттера нет)

https://learn.microsoft.com/en-us/visualstudio/debugger/us...


Правда, это для обычного Дотнета не работало, до версии Коре 3.0, а только для нативного кода на С++.

#14 
alex445 коренной житель28.11.22 14:10
NEW 28.11.22 14:10 
в ответ Программист 28.11.22 12:05

Ну вот у нас как раз такой случай, когда проверять все публичные методы не надо :)

У нас так, есть фабрика объектов, которая конфигурируется в xml. И многие (но не все), объекты (контроллеры, фабрики итд) создаются в рантайме...

Короче, те объекты, которые создаются фабрикой никто никогда не проверяет на null

Что значит "не надо"? Проверки нужны в том числе для предотвращения деградации кода. Может, кто-то поменял код ваших фабрик. Что, теперь вечно раз написанным фабрикам доверять?

#15 
Программист коренной житель28.11.22 14:23
NEW 28.11.22 14:23 
в ответ alex445 28.11.22 13:55
Разве TDD не предполагает 100% покрытия?

Нет, TDD не предполагает 100% покрытия :) Более того, покрытие больше 80% уже где-то на грани экономической целесообразности.

Я знаю только один случай, когда контора требевала и готова была платить за покрытие тестами в 96% - то была контора, которая делала софт для боевых самолетов и вертолетов. Собственно говоря, такие критические к безопасности области и готовы платить за такое.

Все остальные 99% индустрии ИТ не готовы платить за покрытие больше 80%, т.к. после этого рубежа стоимость разработки растет по экспоненте.


#16 
Программист коренной житель28.11.22 14:27
NEW 28.11.22 14:27 
в ответ alex445 28.11.22 13:58
Я имею ввиду, зачем бросать исключение с помощью специального метода, когда можно бросить через throw?

Потому что через throw исключение будет брошено всегда, а Debug.Assert работает только в дебаг сборке. Можно конечно обернуть в #if DEBUG, но это некрасиво :)

#17 
Программист коренной житель28.11.22 14:36
NEW 28.11.22 14:36 
в ответ alex445 28.11.22 14:10
Проверки нужны в том числе для предотвращения деградации кода.

Для этого есть тесты и архитекторы.


Что, теперь вечно раз написанным фабрикам доверять?

Да, этот подход называется доверяй внешним библиотекам. Т.е. исходить нужно из того, что внешние библиотеки работают без ошибок.

Ну и есть еще один подход называется fail-fast. Следуя этому подходу надо давать системе сообщать об ощибках как можно раньше.

#18 
MrSanders коренной житель28.11.22 17:15
NEW 28.11.22 17:15 
в ответ Программист 28.11.22 14:36
Ну и есть еще один подход называется fail-fast. Следуя этому подходу надо давать системе сообщать об ощибках как можно раньше.

И именно по этому подходу и надо бросать эксепшен если нам приходит null там, где его быть не может, сразу. А не там, где он будет использован.

#19 
alex445 коренной житель29.11.22 13:03
NEW 29.11.22 13:03 
в ответ Программист 28.11.22 14:27, Последний раз изменено 29.11.22 13:05 (alex445)

Т.е. Debug не связан с юнит-тестами? Не заменяет их? Не используется в них?


Мне казалось, что натолкав в код всяких Debug.Assert и подобных, можно не писать юнит-тесты для этого кода. По крайней мере в простых случаях, где не требуется замокать сложные объекты. Вот какой смысл писать юнит-тесты для методов типа "вернуть результат деления"? Сложность написания такого метода (например, проверить, чтобы делитель не был равен нулю) примерно равна сложности написания юнит-теста для него. И если мы учли в юнит-тесте все случаи, то и при написании метода можем учесть. Смысл тогда в юнит-тесте в таком случае? Это просто как пример "очевидного кода", который сектанты TDD покроют тестами на 100% в обязон (тем более, что это легко и можно в отчётик написать, что мол весь день работал, хотя по факту делал никому не нужную херню).

#20 
1 2 все