.NET Разработчик
6.51K subscribers
427 photos
2 videos
14 files
2.04K links
Дневник сертифицированного .NET разработчика.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
This media is not supported in your browser
VIEW IN TELEGRAM
День семьдесят седьмой. #TipsAndTricks
Отладка в Visual Studio. Малоизвестные трюки
1. Задать Следующее Выражение
Многие знают о контекстном меню Set Next Statement (Ctrl+Shift+F10) – задать следующее выражение, которое перемещает жёлтую стрелку на нужную строку кода. Вы также можете перетащить её вверх или вниз с помощью мыши. Но начиная с Visual Studio 2017 версии 15.3 есть другой способ.
1. Наведите на линию кода, куда вы хотите переместить жёлтую стрелку.
2. Зажмите CTRL и заметьте, что зелёная иконка Run to Click (Выполнить до этого места) меняется на жёлтую Set Next Statement.
3. Нажмите на иконку, и жёлтая стрелка переместится на эту строку.
4. Это будет выражением, которое выполнится на следующем шаге или при продолжении отладки (F5).
День семьдесят восьмой. #TipsAndTricks
Отладка в Visual Studio. Малоизвестные трюки
2. Прерывание при смене значения
У вас бывали ситуации при отладке, когда вы смотрите на значение свойства в одной точке прерывания, а при переходе к другой точке, значение внезапно меняется? Можно поставить прерывание в мутаторе (set) свойства класса, но тогда оно будет возникать для всех экземпляров этого типа. А вам нужен только один проблемный экземпляр. Вы можете использовать Object ID и условные прерывания, чтобы сузить проблемную область.
1. В точке останова с интересующим экземпляром, щёлкните правой кнопкой на объект и выберите "Make Object ID". Это даст вам обработчик объекта по ссылке "$1".
2. Перейдите в мутатор нужного вам свойства и добавьте условие прерывания "this == $1".
3. Продолжите отладку (F5), и теперь она остановится при изменении свойства этого экземпляра.
4. В стеке вызовов (Call Stack) нажмите дважды на предпоследний пункт. Это перенесёт вас в строку кода, изменяющую значение свойства выбранного вами экземпляра.
Замечание: Object ID ссылается на адрес в памяти, поэтому он изменяется в каждой сессии отладки. Поэтому вам придётся пересоздавать object ID каждый раз. Обработчик ($1) не изменится, поэтому точку останова можно оставить как есть.
👍3
День семьдесят девятый. #TipsAndTricks
Отладка в Visual Studio. Малоизвестные трюки
3. Переприкрепление К Процессу
Иногда при разработке приложения вы не можете просто начать его отладку через F5. Тогда вы можете выбрать пункт меню Прикрепить к Процессу, чтобы отладить его. Зачастую, чтобы таким образом производить отладку, вам нужно осуществлять прикрепление к процессу несколько раз. В Visual Studio 2017 введена новая опция Reattach to Process (Shift+Alt+P), которая позволяет вам начать отладку вашего приложения в один клик, без необходимости проходить через весь диалог прикрепления к процессу каждый раз. Вам всё равно придётся вручную прикрепиться к процессу в первый раз после открытия Visual Studio. Однако в этот диалог добавлен новый поисковый фильтр, чтобы быстрее находить нужное приложение.
Когда вы откроете диалог Прикрепить к Процессу (Attach to Process) - Ctrl+Alt+P, фильтр поиска появится в верхней части списка доступных процессов. Введите имя процесса, к которому вы хотите прикрепиться, и выберите его из списка. Фильтр "прилипчивый", то есть значение фильтра сохранится, когда вы снова откроете этот диалог.
После успешного прикрепления к процессу появится возможность повторного прикрепления к процессу - Reattach to Process (Shift+Alt+P).
Отладчик сначала попробует найти тот же процесс, сопоставив ID и имя процесса. Если он его не найдёт, то поиск продолжится по имени. Если будет найдет один процесс, отладчик прикрепится к нему. Если процессов будет найдено несколько, отладчик покажет диалог Attach to Process для выбора нужного.
Поддерживается возможность переприкрепления к нескольким процессам. Например, если вы использовали диалог Attach to Process для отладки пяти разных процессов в одной сессии отладки, а затем вызвали Reattach to Process, отладчик снова прикрепится ко всем доступным процессам. Для тех, которые не будут найдены, будет выброшено сообщение об ошибке, что прикрепиться к процессу не удалось и почему. Затем откроется диалог Attach to Process, чтобы вы смогли вручную выбрать недостающие процессы или нажать отмена и продолжить только с найденными процессами.

Источник: https://blogs.msdn.microsoft.com/devops/2017/03/07/reattach-to-process-in-visual-studio-2017/
This media is not supported in your browser
VIEW IN TELEGRAM
День восьмидесятый. #TipsAndTricks
Отладка в Visual Studio. Малоизвестные трюки
4. Отображение Потоков в Коде
Отладка многопотокового приложения редко бывает простой, но, когда вы можете посмотреть, на какой строке кода находится каждый поток, становится гораздо лучше.
1. На панели отладчика нажмите "Show Threads in Source" ("Показать Потоки в Коде").
2. В полоске с точками останова появится иконка рядом с каждой строкой кода, на которой остановился хотя бы один поток.
3. Наведите курсор на иконку потока, чтобы увидеть идентификаторы и имена всех потоков, которые остановились на этой строке.
4. Щёлкните правой кнопкой на потоке, чтобы увидеть все доступные действия, вроде остановки потока или выбора активного потока.

Заметьте: Эта функциональность сказывается на производительности и снижает скорость отладки. Рекомендуется отключать её, когда вы её не используете.
This media is not supported in your browser
VIEW IN TELEGRAM
День восемьдесят первый. #TipsAndTricks
Отладка в Visual Studio. Малоизвестные трюки
4. Проход по шагам внутри одного потока
Как часто при отладке многопотокового кода вы останавливаетесь на точке, делаете шаг и внезапно останавливаетесь в другом потоке? Это происходит из-за того, что точка останова установлена и последовательно достигается разными потоками. По умолчанию отладчик будет останавливаться на ней каждый раз, когда её достигает. При выполнении шага все потоки возобновляются, и один из них достигает точки останова перед тем, как завершается шаг в текущем потоке. В следующий раз попробуйте следующее:
1. Деактивируйте или удалите точку останова, на которой останавливается другой поток.
2. Продолжите выполнение - Continue (F5)
3. Проследите как ваш изначальный шаг в первом потоке завершается и теперь он в текущем контексте отладки.
5. Поскольку ваши точки останова удалены или неактивны, вы можете продолжить отладку по шагам в единственном потоке без прерываний.
День восемьдесят второй. #Оффтоп
Понимаю, что, наверное, начинают надоедать однообразные посты о трюках, советах и новинках ещё не вышедшего С# 8. Но ничего не могу поделать. Застрял на нескольких главах Рихтера про сборщик мусора и домены приложения. Интересно, но довольно занудно и длинно написано. Кроме того, сборщик мусора - обширная и достаточно сложная система, что не позволяет хоть как-нибудь сжать 60-страничное содержание главы и уместить его даже в несколько постов. Да и такие сильно технические детали, если и могут быть интересны, то скорее чисто теоретически. Уверен, что 99% разработчиков за всю карьеру не придётся ни разу каким-либо образом вмешиваться в механизм сборщика мусора. Хотя, возможно, я ошибаюсь.
В общем, просто хотел обозначить, что процесс обучения и подготовки к экзамену продолжается, посты из серии "Заметки на полях" ещё будут.
А пока могу порекомендовать серию лекций на YouTube об алгоритмах сортировки от CodeBlog. Сегодня в 20:00 первая лекция про пузырьковую сортировку https://www.youtube.com/watch?v=LxliVjZo1Nc
This media is not supported in your browser
VIEW IN TELEGRAM
День восемьдесят третий. #TipsAndTricks
Отладка в Visual Studio. Малоизвестные трюки
6. Определение Значения Функции без Побочных Эффектов
У вас бывали случаи, когда вы вводили выражение в окно Watch или Immediate, чтобы оценить их значение, а потом вам приходилось сталкиваться с побочными эффектами, потому что статус приложения изменился? Обычно это случается, если выражение вызывает функцию, которая приводит к побочным эффектам (изменяет статус приложения). Это может быть не страшно, если вы об этом знаете, а что, если нет? Вот как оценить значение выражения в C# без риска испортить остальную программу.
1. Добавьте ", nse" (означает “No Side Effects” – Без Побочных Эффектов) после каждого выражения.
2. Это приведёт к интерпретации выражения в отдельной «песочнице», не затрагивая основное приложение.
3. Если выражение не может быть интерпретировано таким образом, вы получите сообщение об ошибке.
4. Если вы уверены, что хотите оценить выражение всё равно, уберите модификатор ", nse" и попробуйте снова.
This media is not supported in your browser
VIEW IN TELEGRAM
День восемьдесят четвёртый. #TipsAndTricks
Отладка в Visual Studio. Малоизвестные трюки
7. Стеки вызовов для всех потоков
Когда есть много потоков, есть и много стеков вызовов. Вам может потребоваться проверить их все, чтобы понять состояние приложения. Вы всегда можете посмотреть на визуальное отображение стека вызовов каждого потока, используя окно Параллельные Стеки (Parallel Stacks) - Debug > Windows > Parallel Stacks. Также в окне Command можно посмотреть текстовое представление стека вызовов каждого потока, которое можно скопировать и вставить.
1. Откройте окно Command (View > Other Windows > Command Window).
2. Введите "Debug.ListCallStacks –AllThreads"
3. Вы также можете использовать популярную команду из WinDBG – "~*k"
4. Теперь все потоки со своими стеками вызовов выведены в окне Command.
This media is not supported in your browser
VIEW IN TELEGRAM
День восемьдесят пятый. #TipsAndTricks
Отладка в Visual Studio. Малоизвестные трюки
8. Наблюдайте за значениями переменных в рекурсивных вызовах функции
Случалось ли вам отлаживать рекурсивные функции, используя многочисленные вызовы Debug.WriteLine()? Иногда бывает сложно решить в голове, как отработает рекурсивная функция. Помочь в этом может окно Parallel Watch, в котором можно отследить изменения переменных в рекурсивном вызове.
1. Откройте окно Parallel Watch (Debug > Windows > Parallel Watch).
2. Нажмите “<Add Watch>” и введите имя нужной переменной.
3. По мере того, как вы проходите по рекурсивным вызовам, вы увидите, как они добавляются в новые строки окна.
This media is not supported in your browser
VIEW IN TELEGRAM
День восемьдесят шестой. #TipsAndTricks
Отладка в Visual Studio. Малоизвестные трюки
9. Установка точки прерывания на функции без поиска её в коде
Иногда бывает нужно отладить код функции, нахождение которой в коде может вызвать трудности. Один из быстрых способов установки точки прерывания в отладчике без поиска файла и строки кода состоит в установке Точки Прерывания на Функции.
1. Нажмите Ctrl+B, либо выберите в меню Debug > New Breakpoint > Function Breakpoint.
2. Введите имя функции, на которой вы хотите установить точку прерывания. Нажмите OK.
3. Начните отладку, и заметьте, что отладчик останавливается на этой магической точке прерывания в файле кода, который вам не пришлось искать вручную.
День восемьдесят седьмой. #ЗаметкиНаПолях
Выражение yield return. Начало
Выражение yield return, вероятно, одна из наименее известных функций в C#. Несмотря на то, что большинство разработчиков о нём слышали, это выражение часто понимается неправильно. Использование ключевого слова yield в методе означает, что этот метод, оператор или акцессор get является итератором. Использование yield исключает необходимость применения явного дополнительного класса для хранения перечисления. Примеры использования:
yield return <expression>; // возвращает каждый элемент по одному 
yield break; // завершает итерацию
Метод-итератор используется путем применения оператора foreach или запроса LINQ. Каждая итерация цикла foreach вызывает метод-итератор. При достижении в методе-итераторе оператора yield return возвращается <expression> и сохраняется текущее расположение в коде. При следующем вызове метода-итератора выполнение возобновляется с этого места.
Небольшой пример, не имеющий особого смысла, но полезный для отладки и понимания работы yield return:
IEnumerable GetNumbers() { 
yield return 1;
yield return 2;
yield return 3;
}
Вызовем его:
foreach(var number in GetNumbers())
Console.WriteLine(number);
Когда вы пройдёте его по шагам в отладке, вы увидите, что текущая строка выполнения прыгает между циклом foreach и выражениями yield return. То есть каждая итерация цикла вызывает метод-итератор GetNumbers до очередного выражения yield return. Значение возвращается вызывающему коду, а позиция в методе-итераторе сохраняется. Выполнение возобновляется с этой позиции при следующем вызове метода-итератора. Это продолжается, пока не будут вызваны все выражения yield return, либо не встретится yield break.

Yield return против обычных циклов
Вот другой пример. Здесь обычный цикл, возвращающий список:
IEnumerable<int> GenerateWithoutYield()
{
var i = 0;
var list = new List<int>();
while (i<5)
list.Add(++i);
return list;
}
foreach(var number in GenerateWithoutYield())
Console.WriteLine(number)
;
Выполнение этого кода по шагам:
1. Вызывается GenerateWithoutYield.
2. Выполняется весь метод и создаётся список.
3. Цикл foreach проходит по всем значениям списка.
4. В результате числа от 1 до 5 выводятся в консоль.

Аналогичный метод с помощью yield return:
IEnumerable<int> GenerateWithYield()
{
var i = 0;
while (i<5)
yield return ++i;
}
foreach(var number in GenerateWithYield())
Console.WriteLine(number);
На первый взгляд этот метод тоже возвращает список из 5 чисел. Но из-за выражения yield код выполняется совершенно по-другому. Метод вообще не возвращает списка. Он создаёт итератор с обещанием вернуть 5 чисел. Несмотря на то, что результат тот же, есть некоторые нюансы выполнения:
1. Вызывается GenerateWithYield.
2. Тип возвращаемого значения этого метода IEnumerable, но это не список, а обещание вернуть последовательность чисел при запросе. Точнее создаётся итератор, позволяющий выполнить это обещание.
3. В каждой итерации цикла foreach вызывается метод-итератор. При достижении выражения yield return возвращается значение, а текущее положение запоминается. Выполнение продолжается с этого места при следующем вызове метода-итератора.
4. В результате в консоли выводятся числа от 1 до 5.

Источники:
-
https://docs.microsoft.com/ru-ru/dotnet/csharp/language-reference/keywords/yield
-
https://www.kenneth-truyers.net/2016/05/12/yield-return-in-c/

Продолжение следует…
День восемьдесят восьмой. #TipsAndTricks
Отладка в Visual Studio. Малоизвестные трюки
10. Изменяйте значения переменных без изменения кода
Сталкивались ли вы при отладке с тем, что значение переменной не то, которое вы ожидали? Либо вы просто хотели бы изменить его, чтобы посмотреть, как поведёт себя код? Вы можете легко это сделать при отладке, просто изменив значение переменной в памяти, используя один из следующих методов, который лучше для вас подходит (см. ниже).
This media is not supported in your browser
VIEW IN TELEGRAM
1. В подсказках данных (DataTips)
Наведите курсор мыши на переменную, чтобы получить всплывающую подсказку со значением переменной. Кликните один раз на значение в подсказке, либо кликните правой кнопкой и выберите "Edit value" ("Изменить значение") из выпадающего меню. Появится курсор редактирования, и вы сможете ввести новое значение переменной.
This media is not supported in your browser
VIEW IN TELEGRAM
2. В окнах Autos, Locals или Watch
Кликните дважды на значении в списке, либо кликните правой кнопкой и выберите "Edit value" ("Изменить значение") из выпадающего меню. Значение станет редактируемым, и вы сможете ввести новое значение переменной.
This media is not supported in your browser
VIEW IN TELEGRAM
3. В окне Immediate
Вы можете использовать кодовый синтаксис для задания переменной нового значения. Например, вы можете ввести “x = 12;”, что изменит значение переменной x в памяти на число 12. Этот метод наиболее подходит в тех случаях, когда вам нужно провести какие-нибудь манипуляции со значением перед тем, как присвоить его переменной.
День восемьдесят девятый. #ЗаметкиНаПолях
Выражение yield return. Примеры использования
1. Кастомные итерации
Допустим, у нас есть список чисел. Мы хотим отобразить все числа, которые больше определённого числа. Обычно нам пришлось бы:
- создать временный список,
- в цикле пройти по исходному списку, сохраняя во временный список подходящие значения,
- пройти по временному списку, чтобы использовать полученные значения.
Мы можем избежать лишней работы, используя yield return:
IEnumerable<int> GetNumbersGreaterThan3(List<int> numbers) 
{
foreach(var nr in numbers)
{
if(nr > 3)
yield return nr;
}
}
foreach(var nr in GetNumbersGreaterThan3(new List<int> {1,2,3,4,5})
Console.WriteLine(nr);
Здесь выполнение кода совершенно другое. Исходный список перебирается всего один раз, и только нужные значения выдаются методом-итератором вызывающему коду (в этом случае циклу foreach).

2. Итерации с сохранением состояния
Метод-итератор, содержащий yield return, сохраняет не только позицию после выдачи очередного значения, но и своё состояние:
IEnumerable<int> Totals(List<int> numbers)
{
var total = 0;
foreach(var number in numbers)
{
total += number;
yield return total;
}
}
foreach(var total in Totals(new List<int> {1,2,3,4,5})
Console.WriteLine(total);
Этот код вернёт значения 1,3,6,10,15. Поскольку состояние сохраняется, переменная total будет сохранять значения между итерациями, тем самым подсчитывая промежуточную сумму чисел списка.

3. Асинхронные потоки
В C# 8 появилась возможность создавать асинхронные потоки, которые были описаны в этом посте: https://t.iss.one/NetDeveloperDiary/88

Источники:
-
https://docs.microsoft.com/ru-ru/dotnet/csharp/language-reference/keywords/yield
-
https://www.kenneth-truyers.net/2016/05/12/yield-return-in-c/

Продолжение следует…
День девяностый. #TipsAndTricks
Отладка в Visual Studio. Малоизвестные трюки
11. Отмечайте потоки и выполняйте их все до выбранного места
При отладке многопоточного кода, вероятно, вам придётся делать точки останова и идти оттуда по шагам, что приведёт к тому, что программа возобновит выполнение других потоков. Иногда для поиска ошибки может быть проще остановить все потоки в одном месте, чтобы проверить состояние программы в это время. Есть простой способ сделать это, отметив потоки и вызвав команду Run Flagged Threads to Cursor (Выполнить Отмеченные Потоки до Курсора).
1. Определите интересующие вас потоки. Вы можете использовать пункт меню Show Threads in Source (Показать Потоки в Коде), окна Parallel Debugging (Параллельная Отладка) или окно Threads (Потоки).
2. Отметьте флажком интересующие вас потоки.
3. Щёлкните правой кнопкой на строке кода, в которой вы хотите исследовать состояние приложения.
4. Из контекстного меню выберите Run Flagged Threads to Cursor (Выполнить Отмеченные Потоки до Курсора).

Замечание: Когда включена опция Show Threads in Source (Показать Потоки в Коде) или видимы любые окна, относящиеся к потокам, это может серьёзно сказаться на производительности вашей сессии отладки. Рекомендуется использовать эти инструменты только когда вы действительно используете их для отладки.
День девяносто первый. #ЗаметкиНаПолях
Выражение yield return. Особенности работы
1. Множественные итерации
Побочным эффектом выражения yield return является то, что множественные вызовы приводят к множественным итерациям:
static void Main(string[] args)
{
var invoices = GetInvoices();
DoubleAmounts(invoices);
Console.WriteLine(invoices.First().Amount);
}

class Invoice { public double Amount { get; set; } }
static IEnumerable<Invoice> GetInvoices()
{
for (var i = 1; i < 11; i++)
yield return new Invoice { Amount = i * 10 };
}
static void DoubleAmounts(IEnumerable<Invoice> invoices)
{
foreach (var invoice in invoices)
invoice.Amount = invoice.Amount * 2;
}
Попробуйте догадаться, что будет выведено в консоль? Несмотря на то, что интуиция подсказывает результат 20, выведено будет 10:
1. Когда выполняется строка var invoices = GetInvoices(); мы не получаем список счетов (Invoice), мы получаем итератор, создающий объекты Invoice.
2. Этот итератор передаётся в метод DoubleAmounts.
3. Внутри метода DoubleAmounts итератор используется для создания объектов Invoice и удвоения суммы счёта (свойство Amount) каждого объекта.
4. Однако все объекты, созданные в методе DoubleAmounts, выбрасываются, поскольку на них нет ссылок из внешнего кода.
5. Когда мы возвращаемся во внешний код, у нас остаётся только ссылка на итератор. Вызывая метод First, мы снова просим создать объект Invoice. Это новый объект, поэтому в результате его свойство Amount будет иметь значение 10.
Поскольку это неочевидное поведение, инструменты вроде Resharper предупреждают вас о множественных итерациях.
2. Отложенное выполнение
Все примеры использования yield return имеют общее свойство. Код выполняется только тогда, когда это необходимо. Механизм паузы/возобновления в методе-итераторе делает это возможным. Используя отложенное выполнение, мы можем делать методы проще, иногда быстрее, а иногда и в принципе делать возможным то, что было невозможно ранее. Например, можно сделать бесконечный генератор чисел и выбирать из него только нужное количество. Весь модуль LINQ в C# построен вокруг отложенного исполнения. Вот как оно повышает эффективность кода:
var dollarPrices = FetchProducts().Take(10)
.Select(p => p.CalculatePrice())
.OrderBy(price => price)
.Take(5)
.Select(price => ConvertTo$(price));
Допустим, у нас 1000 продуктов. Без отложенного выполнения, методу бы пришлось:
1. Выбрать все 1000 продуктов
2. Посчитать цену всех 1000 продуктов
3. Выстроить все продукты по возрастанию цены
4. Перевести все цены в доллары
5. Выбрать 5 продуктов с самой высокой ценой
Используя отложенное выполнение, код:
1. Выбирает 10 продуктов
2. Вычисляет цену 10 продуктов
3. Выстраивает из по возрастанию цены
4. Отбирает первые 5 и переводит их цены в доллары
Несмотря на то, что это выдуманный пример, он чётко показывает, как отложенное выполнение может сильно повысить эффективность. Хотя, заметьте, что пример с отложенным выполнением может делать не то, что вы хотите. В примере выше из всех продуктов отбираются не 10 с наиболее высокой ценой, а 10 первых попавшихся. Поэтому нужно быть внимательным в порядке вызова методов LINQ.

Источники:
-
https://docs.microsoft.com/ru-ru/dotnet/csharp/language-reference/keywords/yield
-
https://www.kenneth-truyers.net/2016/05/12/yield-return-in-c/