.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
День шестьдесят первый. #TipsAndTricks
11. Сниппеты
Многие наверняка знакомы со встроенными в Visual Stodio Сниппетами, но стоит отметить, что они расширяемые. Вы можете добавлять свои сниппеты. Директория хранения сниппетов по умолчанию: %имяпользователя%\Documents\Visual Studio 2017\Code Snippets\Visual C#\My Code Snippets.
Давайте для примера создадим сниппет var для определения переменной:
<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets xmlns="https://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<Title>Var</Title>
<Shortcut>var</Shortcut>
<Description>Code snippet for instantiating an object with full type info</Description>
<Author>Anson Horton</Author>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Declarations>
<Literal>
<ID>type</ID>
<Default>Example</Default>
<ToolTip>The type to construct</ToolTip>
</Literal>
<Literal>
<ID>variable</ID>
<Default>example</Default>
<ToolTip>The variable name</ToolTip>
</Literal>
<Literal>
<ID>args</ID>
<Default></Default>
<ToolTip>The constructor arguments</ToolTip>
</Literal>
</Declarations>
<Code Language="csharp">
<![CDATA[$type$ $variable$ = new $type$($args$);$end$]]>
</Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>
Теперь, когда вы введёте var в файле C# и дважды нажмёте tab, будет вставлен следующий код:
Example example = new Example();
После этого можно клавишей tab перемещаться по литералам и изменять их, при этом все ссылки на одинаковый литерал будут обновляться автоматически. Сниппеты поддерживаются для многих языков в Visual Studio, включая Visual Basic, C#, CSS, HTML, JavaScript, TSQL, TypeScript, Visual C++, XAML и XML.

Источник: https://devblogs.microsoft.com/visualstudio/7-hidden-gems-in-visual-studio-2017/
День шестьдесят второй. #ЗаметкиНаПолях
Исключения. Начало
Конструируя тип, мы заранее пытаемся представить, в каких ситуациях он будет использоваться. Форма определения членов типа (типы данных свойств, параметры методов, возвращаемые значения и т.п.) становится интерфейсом типа. Члены типа определяют допустимые действия с типом и его экземплярами. Если член типа не может решить возложенную на него задачу, должно появляться исключение.
Пример обработки исключений:
try {
// код, требующий корректного восстановления или очистки ресурсов при возникновении исключения
}
catch (InvalidOperationException) {
// обработка исключения типа InvalidOperationException
}
catch (IOException ex) {
// обработка исключения типа IOException
// информация об исключении будет в переменной ex
}
catch {
// обработка остальных типов исключений
}
finally {
// код очистки ресурсов после операций блока try
// этот код исполняется ВСЕГДА
}
// код после блока finally выполняется, если не было сгенерировано исключений, либо если они были перехвачены блоками catch, а новых не генерировалось
Свойства исключений:
1. Исключения представляют собой типы, производные в конечном счете от System.Exception.
2. Блок try используется для выполнения таких инструкций, которые могут создавать исключения. Блок try должен быть связан хотя бы с одним блоком catch или finally.
3. Когда внутри такого блока try возникает исключение, поток управления переходит к первому подходящему обработчику исключений в стеке вызовов. В C# ключевое слово catch обозначает обработчик исключений. Если исключения не возникает, код в блоке catch не выполняется.
4. Если блок catch определяет переменную исключения, ее можно использовать для получения дополнительных сведений о типе созданного исключения.
5. Просмотр блоков catch осуществляется сверху вниз, поэтому исключения должны указываться в порядке от более специализированных к их базовым классам (если они есть) и наконец, к классу System.Exception. Иначе компилятор сообщит об ошибке, т.к. более узкоспециализированные блоки окажутся недостижимыми.
6. Если для созданного исключения не существует обработчиков, выполнение программы прекращается с сообщением об ошибке.
7. Программа может явным образом создавать исключения с помощью ключевого слова throw.
8. Объекты исключения содержат подробные сведения об ошибке, например состояние стека вызовов и текстовое описание ошибки.
9. Код в блоке finally выполняется даже в том случае, если создано исключение. Используйте блок finally, чтобы высвободить ресурсы, например закрыть потоки и файлы, которые были открыты внутри блока try. Блок finally помещается после всех блоков catch. Одному блоку try может соответствовать только один блок finally.

Источники:
- Джеффри Рихтер “CLR via C#”. 3-е изд. – СПб.: Питер, 2012. Глава 20.
-
https://docs.microsoft.com/ru-ru/dotnet/csharp/programming-guide/exceptions/
День шестьдесят третий.
Visual Studio 2019
Visual Studio 2019 стала доступна для скачивания. С её помощью вы и ваша команда сможете стать ещё более продуктивными в проектировании текущих и будущих проектов в среде, для которой важно каждое нажатие кнопки.
Версия 2019 содержит улучшения 2017й версии в нескольких областях. Она помогает вам быстро получить доступ к коду, упрощая создание клонов в репозитории Git или открытие существующих проектов или папок. Также улучшения коснулись экрана выбора шаблонов при старте нового проекта. При написании кода вы увидите, что улучшилась навигация по коду и добавлено много вариантов рефакторинга, а также показателей состояния документа и очистка кода в один клик с применением сразу нескольких правил рефакторинга. Также улучшена отладка, включая точки останова на данных в приложениях .Net Core, которые помогут вам прерывать выполнение при изменении выбранных вами значений. Кроме того, добавлен помощник в написании кода, основанный на искусственном интеллекте, IntelliCode.
Эти нововведения будут работать как на существующих проектах, так и в новых, от кроссплатформенных приложений на C++ до мобильных приложений .Net для Android и iOS, написанных на Xamarin, до облачных сервисов на Azure. Цель Visual Studio 2019 – поддерживать эти проекты от разработки, через тестирование и отладку до развёртывания, минимизируя для вас необходимость переключаться между разными приложениями, порталами и сайтами.
Скачать Релиз-кандидат Visual Studio 2019 можно здесь: https://visualstudio.microsoft.com/ru/downloads/

Источник: https://devblogs.microsoft.com/visualstudio/visual-studio-2019-code-faster-work-smarter-create-the-future/
День шестьдесят четвёртый. #ЗаметкиНаПолях
Исключения. Продолжение
Советы по обработке исключений
1. Если блок catch определяет переменную исключения, ее можно использовать для получения дополнительных сведений о типе созданного исключения. Эту переменную обычно стоит рассматривать как доступную только для чтения, однако можно добавлять нужную информацию в коллекцию её свойства Data.
2. Программа может явным образом создавать исключения с помощью ключевого слова throw.
3. Объём блока кода внутри try зависит от управления состоянием. Если внутри блока вы собираетесь выполнить набор операций, каждая из которых может стать причиной исключения одного и того же типа, но при этом вы хотите обработать эти исключения по-разному, имеет смысл поместить операции в разные блоки try.
4. При отладке в Visual Studio для просмотра выброшенного исключения нужно добавить в окно Watch специальную переменную $exception.
5. Если обнаружится, что состояние приложения осталось испорченным даже после восстановительных операций в блоках catch или finally, имеет смысл его удалить, чтобы не создавать дополнительных проблем. Это делается методом AppDomain.Unload. После этого приложение перезагружается, чтобы состояние инициализировалось нормально. Если состояние кажется настолько плохим, что имеет смысл завершить работу приложения, используйте статический метод Environment.FailFast. Он завершает процесс без выполнения активных блоков try/finally и без вызовов метода Finalize.

Приёмы работы с исключениями:
1. Активно используйте блоки finally для очистки ресурсов.
- В C# блоки try/finally автоматически создаются компилятором для некоторых конструкций: в lock отключается блокировка, в using вызывается метод Dispose объекта, в foreach вызывается метод Dispose объекта IEnumerator, в деструкторах вызывается метод Finalize базового класса.
2. Не надо перехватывать все исключения.
- К примеру, код библиотеки не может знать, как он будет использоваться в различных приложениях. Исключение должно передаваться вверх по стеку вызовов и обрабатываться кодом приложения. Допустимо перехватывать исключения System.Exception только при условии их повторной генерации в конце блока catch.
3. Корректное восстановление после исключения.
- Иногда заранее известны типы исключений, источником которых может стать метод. Только полностью осознавая обстоятельства, вызывающие конкретные типы исключений, можно перехватывать их и позволять приложению продолжать работу после восстановления.
4. Отмена незавершённых операций при невосстановимых исключениях.
- Когда бизнес-логика требует отменить все ранее выполненные действия при возникновении исключения (банковская транзакция, сериализация группы объектов, транзакция в базе данных и т.п.), имеет смысл перехватывать ВСЕ возможные исключения, отменять сделанные изменения, но обязательно выбрасывать исключение повторно.
5. Скрытие деталей реализации для сохранения контракта
- Иногда бывает полезно после перехвата одного исключения, выбросить исключение другого типа для сохранения деталей реализации. Например, создать исключение RecordNotFoundException для операций поиска в файле или базе данных и выбрасывать его вместо исключений типа FileNotFound, IOException или исключений, связанных с доступом к базе данных.
- При использовании этого приёма стоит перехватывать только те исключения, обстоятельства возникновения которых вы хорошо понимаете. Здесь мы обманываем вызывающий код сообщая неправду о том, что пошло не так, и о месте сбоя. Трассировка стека укажет только место выброса исключения RecordNotFoundException, но не изначального. Это может затруднить отладку.

Источники:
- Джеффри Рихтер “CLR via C#”. 3-е изд. – СПб.: Питер, 2012. Глава 20.
-
https://docs.microsoft.com/ru-ru/dotnet/csharp/programming-guide/exceptions/
👍1
День шестьдесят пятый. #TipsAndTricks
13. Просмотр производных типов
Иногда бывает полезно посмотреть все типы производные от базового класса. Это особенно актуально для типов из больших фреймворков вроде .NET Framework. Это можно сделать в окне Object Browser. Хитрость в том, что по умолчанию просмотр объектов ограничен вашим решением («My Solution» в списке Browse). Но значение можно изменить. Например, посмотрим типы из библиотеки .NET Framework 4.7.2 (см. рисунок).
Теперь, если вы хотите посмотреть типы производные от System.IO.Stream, просто найдите этот тип, используя строку поиска и разверните папку «Derived Types». Возможно, придётся немного подождать результатов.
Это работает для любой сборки или фреймворка. Но, чтобы использовать это для ссылок, подключённых к вашему проекту, сначала нужно добавить их в отдельный набор «custom component set», нажав на … справа от выпадающего списка Browse.
Источник: https://devblogs.microsoft.com/visualstudio/7-hidden-gems-in-visual-studio-2017/
👍2
День шестьдесят шестой. #ЗаметкиНаПолях
Исключения. Продолжение
Генерация исключений
При реализации своего метода нужно генерировать исключение, если метод не в состоянии решить возложенную на него задачу. При этом важно учесть два фактора:
1. Понять, к какому типу, производному от System.Exception будет относиться ваше исключение. Можно использовать один из типов FCL, либо создать собственный тип. При этом в иерархии типов должно быть как можно меньше базовых классов, т.к. перехват ошибок базового класса также перехватит и ошибки всех производных классов, а это может быть нежелательно.
2. Решить, какое сообщение должно быть передано конструктору исключения. Исключение должно содержать детальную информацию, почему метод не смог решить свою задачу. Обычно конечные пользователи приложения не имеют доступа к исходному коду, а приложения скрывают от них детали ошибок, поэтому в сообщение можно смело помещать всю техническую информацию, необходимую для устранения дефекта.
Выражение throw
Начиная с C# 7.0 throw можно использовать в контекстах, которые ранее не поддерживались:
1. Тернарный оператор
До C# 7.0 нужно было использовать if/else:
private static void DisplayFirstNumber(string[] args)
{
string arg = args.Length >= 1 ? args[0] :
throw new ArgumentException("Массив пустой");
}
2. Оператор объединения с NULL
public string Name
{
get => name;
set => name = value ??
throw new ArgumentNullException("Имя не может быть null", nameof(value));
}

3. Лямбда-выражение или лямбда-метод
DateTime ToDateTime(IFormatProvider provider) => 
throw new InvalidCastException("Преобразование в DateTime не поддерживается.");

Источники:
- Джеффри Рихтер “CLR via C#”. 3-е изд. – СПб.: Питер, 2012. Глава 20.
-
https://docs.microsoft.com/ru-ru/dotnet/csharp/language-reference/keywords/throw
День шестьдесят седьмой. #Оффтоп
Нашёл ещё один интересный канал на YouTube про C# и .Net разработку - IAmTimCorey. https://www.youtube.com/user/IAmTimCorey/ Сразу предупреждаю, все видео на английском, поэтому кто ещё не изучил... это ваши проблемы)))
Тим - разработчик софта, тренер и консультант, имеющий награды Microsoft MVP, PMP и MCP, а также Intel Software Innovator. Цель канала - упростить изучение разработки ПО. Видео на канале самые разнообразные: базовые понятия, инструменты и советы по C#, паттерны проектирования, создание приложения на C# с нуля, связанные технологии (ASP.NET MVC, Azure, SQL Data Tools и т.п.).
Видео не из коротких (от 40 до 90 минут), потому что объясняется всё достаточно подробно. Но и нельзя сказать, что сильно затянуто. Я смотрю для повторения темы либо фоном, либо на скорости 1.5x. Кроме того, есть подборка Weekly Challenge. Короткое, на пару минут, видео с заданием по C# на определённую тему. Можно решить самому, а потом посмотреть предложенное решение по ссылке в описании к видео. Самое главное, что канал не заброшен. Последнее Weekly Challenge совсем свежее, выдано 4 апреля.
Кроме того, у Тима есть сайт с текстовыми версиями и решениями Weekly Challenge, а также блогом: https://www.iamtimcorey.com
День шестьдесят восьмой. #Оффтоп
Я уже писал, что не считаю себя труЪ программистом. В моём понимании настоящие программисты работают над созданием сложных систем и эффективных алгоритмов, создают операционные системы и языки программирования, а также программы ИИ, обыгрывающие человека в шахматы или го. А остальные, и я в том числе, разработчики, которые пользуются плодами первых. И 99% нашей работы, давайте по-честноку, сбор целого из готовых кусочков и небольшая доработка напильником, без сильного напряжения мозга. Если что, я не хотел никого обидеть)))
А поэтому мне всегда нравились ролики, в которых описываются либо базовые алгоритмы (о них, может, сделаю отдельную серию постов), либо решения интересных задач из реального мира. Вот, к примеру, наткнулся в рекомендациях ютуба на видео из курса MIT 6.S095 «Programming for the Puzzled». https://www.youtube.com/watch?v=auK3PSZoidc
В часовом видео, оригинально названном «Вы больше никогда не захотите играть в судоку», описан алгоритм нахождения решения судоку. Сначала прямо в лоб, затем применяются некоторые усовершенствования, чтобы оптимизировать его работу. И какой бы трудной задача ни казалась изначально, в реальности всё решение сводится к 40 строчкам кода и не содержит взрывающих мозг сложностей. Кстати, прекрасный пример красивого и эффективного применения рекурсии. Код в видео написан на Python, но, думаю, что даже тем, кто не знаком с синтаксисом этого языка, будет в принципе понятна суть. Да, и, как это всегда бывает с самыми интересными материалами по программированию, видео на английском.
День шестьдесят девятый. #CSharp8
Новые функции в C# 8.
1. Реализация по Умолчанию Интерфейсных Методов
Эта функция помогает вам добавлять функциональность к интерфейсам ваших библиотек, при этом поддерживая обратную совместимость с кодом, написанным для предыдущих версий этих интерфейсов.
Сейчас после того, как вы опубликовали интерфейс, «игра окончена». Вы не можете добавлять члены к интерфейсу без того, чтобы сломать все существующие его реализации.
C# 8.0 позволяет вам предоставить тело члена интерфейса. Поэтому, если кто-то не реализует этот член (возможно, потому что его ещё не существовало на момент написания кода), они просто будут использовать реализацию по умолчанию.
interface ILogger
{
void Log(LogLevel level, string message);
// Новый перегруженный метод
void Log(Exception ex) => Log(LogLevel.Error, ex.ToString());
}
class ConsoleLogger : ILogger
{
public void Log(LogLevel level, string message) { ... }
// Метод Log(Exception) получает реализацию по умолчанию
}
Класс ConsoleLogger не имеет реализации перегруженного метода Log(Exception) интерфейса ILogger, поскольку был объявлен до внесения изменений в интерфейс. Теперь вы можете безопасно добавлять новые члены в существующие публичные интерфейсы, если вы предоставляете реализацию по умолчанию для уже существующих реализаторов.
Преимущества
Вы можете добавлять функциональность к интерфейсам, не нарушая совместимость с предыдущими версиями этих интерфейсов.
Недостатки
Это нужно использовать с осторожностью. В противном случае можно легко нарушить принципы единственной ответственности. Подумайте, прежде чем использовать этот инструмент, нет ли другого, более элегантного решения вашей проблемы.

Источник: https://www.c-sharpcorner.com/article/c-sharp-8-features/
👍1
День семидесятый. #ЗаметкиНаПолях
For Или Foreach в C#
Многие программисты до сих пор путаются, когда использовать цикл for, а когда foreach. Рассмотрим перебор элементов коллекции в обоих циклах.
List<Employee> employees = GetEmployeesList();
Цикл for для списка будет выглядеть примерно так:
for (int i = 0; i < employees.Count; i++)
{
Console.WriteLine(employees[i].Age);
}
Заметьте, что вы можете напрямую обращаться к элементам списка по индексу.
Внутри цикла foreach вы можете напрямую обращаться к каждому элементу:
foreach(Employee emp in Employees)
{
Console.WriteLine(emp.Age);
}
Заметьте, что цикл foreach создаёт копию коллекции, по которой вы проходите. Это означает, что, если вы хотите выполнить операцию присваивания на элементе коллекции, вы не сможете этого сделать:
int[] numbers = { 1, 2, 3, 4, 5, 6 };
foreach (int item in numbers)
{
item++;
}
Этот код приводит к ошибке CS1656: Cannot assign to 'item' because it is a 'foreach iteration variable' (Невозможно выполнить присваивание переменной item, поскольку это ‘переменная итерации цикла foreach’).
Дело в том, item здесь не ссылается на элемент массива, это просто временная переменная, и вы не можете присваивать ей значения.
Кроме того, в общем случае foreach требует больше времени и дополнительную память, по сравнению с for, поскольку использует методы GetEnumarator() и Next() интерфейса IEnumarable.
Подводя итог, когда вам нужна простота чтения кода, используйте foreach, когда требуется доступ к элементам внутри цикла, а также критична производительность, используйте for.

Источник: https://www.c-sharpcorner.com/article/for-vs-foreach-in-c-sharp/
День семьдесят первый. #CSharp8
Новые функции в C# 8.
2. Обнуляемый Ссылочный Тип
В C#8 можно объявлять обнуляемый контекст с помощью аннотации #nullable. Внутри него любые переменные ссылочного типа считаются необнуляемыми. Если вы хотите присвоить ссылочному типу null, нужно явно объявить обнуляемый тип переменной с помощью <тип>?.
Для необнуляемых ссылочных типов компилятор использует анализ потока, чтобы убедиться, что локальные переменные инициализированы не-null значением. Все поля должны быть инициализированы в конструкторе. Компилятор выдаёт предупреждение, если переменная не объявляется вызовом любого из доступных конструкторов или инициализатором.
Обнуляемые ссылочные типы не проверяются на присваивание null, однако компилятор использует анализ потока, чтобы убедиться, что переменная обнуляемого типа не содержит null, прежде чем к ней обращается код или она присваивается переменной необнуляемого типа.

Источник: https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8
День семьдесят второй. #CSharp8
Новые функции в C# 8.
3. Продвинутое Сопоставление с Шаблоном
Сопоставление выражения с шаблоном предоставляет инструменты для сравнения по форме связанных, но разных типов данных. В C# 7.0 был представлен синтаксис шаблонов типа и шаблонов констант, используя выражения is и switch.
C# 8.0 расширяет эту функциональность, так что вы можете использовать больше шаблонных выражений в разнообразных местах вашего кода. Используйте эти функции, когда ваши данные и функционал разделены, когда ваши алгоритмы не зависят от типа объекта во время выполнения. В дополнение к новым шаблонам в новых местах, в C# 8.0 вводятся рекурсивные шаблоны. Результат любого шаблона выражения – это выражение. Рекурсивный шаблон – это просто шаблон выражения, применённый к результату другого шаблона выражения.
Преимущества
Рекурсивное сопоставление с шаблоном помогает вам разбить структуры данных на компоненты и использовать их в очень удобном и компактном синтаксисе. Несмотря на то, что сопоставление с шаблоном эквивалентно последовательности выражений if-then, оно помогает вам писать код в стиле функционального программирования.
Недостатки
В сложных выражениях синтаксис может быть хитрым и сложным для понимания.

В следующем примере шаблон выражения используется для сопоставления структуры с шаблоном:
var point = new 3DPoint(1, 2, 3); //x=1, y=2, z=3  
if (point is 3DPoint(1, var myY, _))
{
// Этот код будет выполнен, только если point.X == 1
// myY – это новая переменная,
// которая будет доступна только в этом блоке
// Третий член объекта игнорируется спецсимволом _
}
Шаблоны свойств
Шаблон свойства позволяет вам сопоставить свойства исследуемого объекта. В следующем примере метод рассчитывает налог с продаж в зависимости от штата (свойство State извлекается из передаваемого объекта типа Address). Расчёт этого налога не является ответственностью класса Address, поскольку он может изменяться гораздо чаще, чем структура адреса:
public static decimal ComputeSalesTax(Address location, decimal salePrice) =>
location switch
{
{ State: "WA" } => salePrice * 0.06M,
{ State: "MN" } => salePrice * 0.075M,
{ State: "MI" } => salePrice * 0.05M,
// ...
_ => 0M
};
Позиционные шаблоны
Некоторые типы включают метод Deconstruct, который деконструирует свойства в отдельные переменные. Когда доступен метод Deconstruct, вы можете использовать позиционные шаблоны для сопоставления свойств с шаблоном. Рассмотрим следующий класс точки с координатами X и Y:
public class Point
{
public int X { get; }
public int Y { get; }
public Point(int x, int y) => (X, Y) = (x, y);
public void Deconstruct(out int x, out int y) =>
(x, y) = (X, Y);
}
Также у нас имеется перечисление, представляющее собой позицию точки в системе координат: неизвестна, в центре, в первом квадратне, во втором квадранте, …, на оси координат:
public enum Quadrant
{
Unknown,
Origin,
One,
Two,
Three,
Four,
OnBorder
}
Следующий метод использует позиционный шаблон для извлечения значений x и y, а также использует условие when для определения квадранта:
static Quadrant GetQuadrant(Point point) => point switch
{
(0, 0) => Quadrant.Origin,
var (x, y) when x > 0 && y > 0 => Quadrant.One,
var (x, y) when x < 0 && y > 0 => Quadrant.Two,
var (x, y) when x < 0 && y < 0 => Quadrant.Three,
var (x, y) when x > 0 && y < 0 => Quadrant.Four,
var (_, _) => Quadrant.OnBorder,
_ => Quadrant.Unknown
};
Шаблон пустого кортежа (_, _) соответствует случаю, когда либо x, либо y равно 0, но не оба. Выражение switch должно либо возвращать значение, либо выбрасывать исключение. Если ни один из вариантов не находит соответствия, выражение switch выбрасывает исключение. Компилятор выдаст предупреждение, если вы не укажете все возможные варианты в выражении switch.

Источники:
-
https://www.c-sharpcorner.com/article/c-sharp-8-features/
-
https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8
День семьдесят третий. #CSharp8
Новые функции в C# 8.
4. Асинхронные потоки
Начиная с C# 8.0 вы можете создавать и потреблять потоки асинхронно. Метод, возвращающий асинхронный поток, имеет три свойства:
1. Объявляется с модификатором async.
2. Возвращает IAsyncEnumerable<T>.
3. Содержит выражения yield return, чтобы возвращать последующие элементы из асинхронного потока.
Потребитель асинхронного потока должен добавить ключевое слово await перед ключевым словом foreach при переборе элементов потока. Добавление ключевого слова await требует, чтобы метод, который перечисляет элементы асинхронного потока, был объявлен с модификатором async и возвращал тип, разрешенный для асинхронного метода. Обычно это означает возврат типа Task или Task<TResult>, а также может быть ValueTask или ValueTask<TResult>. Метод может как потреблять, так и производить асинхронный поток, что значит, что он будет возвращать IAsyncEnumerable<T>.
Следующий код генерирует последовательность от 0 до 19 с ожиданием 100ms между генерациями каждого числа. Затем можно перебрать последовательность, используя выражение await foreach:
using System;
using System.Threading.Tasks;
using System.Collections.Generic;

static async Task Main(string[] args)
{
await foreach (var number in GenerateSequence())
{
Console.WriteLine(number);
}
}
public static async System.Collections.Generic.IAsyncEnumerable<int> GenerateSequence()
{
for (int i = 0; i < 20; i++)
{
await Task.Delay(100);
yield return i;
}
}

Заметьте, что для выполнения этого кода требуется установить SDK .NET Core 3.0 и использовать его в качестве Target framework в свойствах проекта. А чтобы он появился в этом списке, возможно, потребуется отметить флажок “Use previews of the .NET Core SDK” в Tools > Options > Projects and Solutions > .NET Core и перезагрузить Visual Studio.

Преимущества
Асинхронные потоки предоставляют замечательную возможность представить асинхронные источники данных, которые могут контролироваться потребителем. Например, при загрузке данных из сети, мы хотели бы создать асинхронную коллекцию, которая возвращает данные по частям, как только те становятся доступными.

Источники:
-
https://www.c-sharpcorner.com/article/c-sharp-8-features/
-
https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8
День семьдесят четвёртый. #CSharp8
Новые функции в C# 8.
5. Индексы и диапазоны
Диапазоны и индексы предоставляют краткий синтаксис указания поддиапазонов элементов массива или типов Span<T> или ReadOnlySpan<T>.
Вы можете указать индекс с конца, используя оператор ^. Выражение array[2] означает "2й элемент с начала". Теперь можно использовать array[^2], что означает "2й элемент с конца". Индекс ^0 означает "конец", то есть индекс, следующий за последним элементом.
Вы можете указать диапазон с помощью оператора ... Например, 0..^0 обозначает весь диапазон массива: 0 с начала до, но не включая, 0 с конца. Любой из операндов оператора может быть как индексом "с начала", так и "с конца". Более того, любой из операндов может быть опущен. По умолчанию 0 – начальный индекс, ^0 – конечный индекс.
Рассмотрим следующий массив строк, обозначенный индексами с начала и с конца:
var words = new string[]
{
// с начала с конца
"The", // 0 ^9
"quick", // 1 ^8
"brown", // 2 ^7
"fox", // 3 ^6
"jumped", // 4 ^5
"over", // 5 ^4
"the", // 6 ^3
"lazy", // 7 ^2
"dog" // 8 ^1
};
Индекс каждого элемента усиливает концепцию "с начала" и "с конца", а диапазоны исключают конец диапазона. "Старт" массива – это первый элемент, а "конец" массива находится за последним элементом.
Вы можете получить последнее слово с помощью индекса ^1:
Console.WriteLine($"The last word is {words[^1]}");
// выведет "dog"
Следующий код создаёт поддиапазон со словами "quick", "brown" и "fox". Он включает элементы с words[1] до words[3]. Элемент words[4] не входит в диапазон.
var quickBrownFox = words[1..4];
Следующий код создаёт поддиапазон со словами "lazy" и "dog". Он включает элементы words[^2] и words[^1]. Конечный индекс words[^0] не включается:
var lazyDog = words[^2..^0];
Следующие примеры создают диапазоны, открытые с начала, с конца и с обоих концов:
var allWords = words[..]; // содержит все слова
var firstPhrase = words[..4]; // слова от "The" до "fox"
var lastPhrase = words[6..]; // слова "the", "lazy" и "dog"
Вы также можете объявлять диапазоны как переменные, которые потом могут использоваться внутри квадратных скобок:
Range phrase = 1..4;
var text = words[phrase];
Заметьте, что, если вы укажете неверный диапазон, где начальное значение больше конечного, то будет выброшено исключение времени выполнения System.OverflowException, например:
var invalidRange = words[5..2];
var invalidEnd = words[6..^4];
При этом компилятор не выдаёт ошибок или предупреждений во время компиляции, даже если вы явно указываете большее начальное значение (по крайней мере в нынешней версии).

Заметьте, что для использования диапазонов требуется установить SDK .NET Core 3.0 и использовать его в качестве Target framework в свойствах проекта. А чтобы он появился в этом списке, возможно, потребуется отметить флажок “Use previews of the .NET Core SDK” в Tools > Options > Projects and Solutions > .NET Core и перезагрузить Visual Studio.

Источник:
https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8
День семьдесят пятый. #CSharp8
Новые функции в C# 8.
6. Декларации using
Декларация using – это определение переменной, которому предшествует ключевое слово using. Оно сообщает компилятору, что определяемая переменная должна быть высвобождена в конце текущего блока кода:
static void WriteLinesToFile(IEnumerable<string> lines)
{
using var file = new System.IO.StreamWriter("WriteLines2.txt");
foreach (string line in lines)
{
file.WriteLine(line);
}
// переменная file высвобождается здесь
}
В предыдущем примере переменная file освобождается перед тем, как в коде встречается закрывающая скобка метода. Это конец блока, в котором она определена. Предыдущий пример аналогичен использованию блока using:
using (var file = new System.IO.StreamWriter("WriteLines2.txt"))
{
// …
}
В обоих случаях компилятор вызывает метод Dispose(). Компилятор выбросит ошибку, если выражение в операторе using не является освобождаемым.

Источник: https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8
День семьдесят шестой. #CSharp8
Новые функции в C# 8.
7. Статические локальные функции
Вы теперь можете добавлять модификатор static к локальным функциям, чтобы убедиться, что локальная функция не включает (не ссылается) на переменные из обрамляющего её блока кода. Если это произойдёт, будет выброшено исключение CS8421 "A static local function can't contain a reference to <variable>." ("Статическая локальная функция не может ссылаться на <имя переменной>.").
Рассмотрим следующий код. Локальная функция LocalFunction ссылается на переменную y, объявленную в обрамляющем её коде (методе M). Поэтому LocalFunction не может быть объявлена статической:
int M()
{
int y;
LocalFunction();
return y;
void LocalFunction() => y = 0;
}
Следующий код содержит статическую локальную функцию. Она может быть объявлена статической, поскольку она не содержит никаких ссылок на переменные из обрамляющего её кода:
int M()
{
int y = 5;
int x = 7;
return Add(x, y);
static int Add(int left, int right) => left + right;
}

Источник: https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8
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