.NET Разработчик
6.51K subscribers
425 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
День шестьдесят девятый. #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
День семьдесят первый. #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