.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
День 1743. #ВопросыНаСобеседовании #Многопоточность
Самые часто задаваемые вопросы на собеседовании по C#

26. Объясните концепцию ThreadLocal и секционирования данных, и как это может помочь улучшить общую производительность многопоточного приложения?

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

В C# можно использовать класс ThreadLocal<T> для объявления локальной переменной потока:

var localSum = new ThreadLocal<int>(() => 0);

// Каждый поток может безопасно использовать и изменять свою localSum без синхронизации
localSum.Value += 1;

Свойство IsValueCreated локальной переменной потока возвращает true, если переменная уже была инициализирована в этом потоке. Так можно определить, что управление вернулось в созданный ранее поток.

Секционирование данных
Это метод обработки данных, при котором большой набор данных делится на более мелкие независимые части. Затем каждая часть обрабатывается отдельным потоком параллельно. Секционирование данных позволяет лучше использовать системные ресурсы, уменьшает конфликты и помогает повысить общую производительность параллельных алгоритмов.

Перераспределение может выполняться статически или динамически, в зависимости от конкретной задачи и целей приложения. Parallel.ForEach и Parallel LINQ (PLINQ) — это два примера встроенных механизмов .NET, которые используют секционирование данных для более эффективного выполнения параллельных операций.

Пример разделения данных с использованием Parallel.ForEach:

var data = new List<int> { … };
Parallel.ForEach(data, item =>
{
// обработка элемента
});

Итого
Локальное хранилище потоков и секционирование данных — это два способа значительно повысить производительность и эффективность многопоточных приложений на C#. Они помогают минимизировать конфликты, уменьшить накладные расходы на блокировку и лучше использовать доступные системные ресурсы. Очень важно выбрать подходящий метод, исходя из характера проблемы и задействованных алгоритмов.

Источник: https://dev.to/bytehide/c-multithreading-interview-questions-and-answers-4opj
👍21
День 1744. #Оффтоп
У Интерфейсов Могут Быть Приватные Методы

Вот вам немного бесполезных знаний. Интерфейсы могут иметь приватные методы.

Это связано с функцией C#8: «реализация интерфейса по умолчанию». Она позволяет нам определить реализацию по умолчанию для метода в интерфейсе. Это полезно, когда мы хотим добавить новый метод к интерфейсу, но не хотим разрушать все классы, реализующие этот интерфейс. Что-то вроде этого:

public interface IMyInterface
{
public void MyMethod()
{
// Что-то делаем
}
}

public class MyClass : IMyInterface
{
// Не надо реализовывать MyMethod
}

Но при использовании мы не можем вызвать MyMethod из MyClass:
cs 

var myClass = new MyClass();
myClass.MyMethod();

Код выше не скомпилируется. Метод надо вызвать вот так:

IMyInterface myInterface = myClass;
myInterface.MyMethod();

И чтобы ещё больше всё запутать, мы также можем провести рефакторинг приватных членов интерфейса:

public interface IMyInterface
{
public void MyMethod()
{
MyPrivateMethod();
}

private void MyPrivateMethod()
{
// что-то делаем
}
}

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

Источник: https://steven-giesel.com/blogPost/03957748-73b3-40f9-a9f7-cc95501a3a8e
👍16
День 1745. #Карьера #ProjectManagement
Как Дать Эффективную Обратную Связь Сложному Человеку. Начало

Если слишком долго позволять трудным в общении людям делать всё по-своему, это может нанести непоправимый ущерб. Предоставление им обратной связи будет неэффективным, если она не будет правильной. Рассмотрим стратегии предоставления обратной связи таким трудным людям.

В коллективах бывают умные, талантливые и результативные люди. Они умеют решать сложные проблемы, и на них можно положиться для достижения цели. Но что, если они хороши только при самостоятельном выполнении работы? Что, если команде трудно общаться и сотрудничать с ними, что снижает продуктивность и результативность команды? Что, если они:
- Унижают других, когда их идеи неудачны.
- Занимают оборонительную позицию, когда другие с ними не согласны.
- Устанавливают высокие стандарты и ожидают, что другие будут им соответствовать.
- Выражают недовольство криком и гневом.
- Перебивают других и не позволяя им говорить.
- Ожидают особого к себе отношения.

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

Роберт Саттон в книге «Хороший босс, плохой босс» говорит, что лучшие начальники делают больше, чем просто заряжают людей, набирают и воспитывают работяг. Они устраняют негатив, потому что даже один токсичный человек или несколько деструктивных поступков могут навредить множеству хороших людей и конструктивных действий.

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

Именно здесь большинство менеджеров допускают ошибки. Они либо слишком долго позволяют трудным людям делать всё по-своему, причиняя непоправимый ущерб, либо дают обратную связь в неправильной форме. Тогда трудные люди отказываются её принимать, занимают оборонительную позицию и могут даже ожесточиться, что только больше усложнит работу с ними.

Вот 4 стратегии дать обратную связь сложному человеку, чтобы он не бросил работу или не создал ещё больше проблем.

1. Тщательно выбирайте слова
Для сложного человека определённые слова в обратной связи вызывают сильные негативные чувства, которые заставляют его защищаться:
- Обобщающие, типа «всегда» и «никогда».
- Навязывающие, как «не могу», «не должен», «должен», «подчиняться».
- Бросающие вызов: «плохой», «требовательный», «непрофессиональный», «грубый».
- Осуждающие: «ошибка», «неудача», «неприемлемо».

Селеста Хэдли пишет в книге «Нам надо поговорить»: «Высокообразованные люди склонны придавать большое значение логике и преуменьшать важность эмоций. Конечно, вы не можете выиграть дебаты с помощью эмоциональных аргументов, но разговор — это не дебаты, а люди по своей сути нелогичны. Мы эмоциональные существа. Удалить или попытаться удалить эмоции из вашего разговора — значит извлечь большую часть смысла и важности».

Будьте внимательны, тщательно обдумайте эффект того, что вы говорите, и избегайте эмоционально заряженных слов, которые делают обратную связь неэффективной.

Например:
Вместо: Вы всегда опаздываете на встречи. Ваше поведение испортит и других членов команды.
Скажите: На последних двух встречах я заметил, что вы приходили через 10-15 минут после начала. Вы можете приходить на собрания вовремя и подать хороший пример другим?

Вместо: Вы должны позволить другим высказываться в обсуждениях. Перебивать их – это грубо.
Скажите: Выслушивание различных точек зрения поможет нам принимать более правильные решения. Давайте попробуем стимулировать участие других в обсуждениях.

Продолжение следует…

Источник:
https://www.techtello.com/give-feedback-to-a-difficult-person/
👍11
День 1746. #Карьера #ProjectManagement
Как Дать Эффективную Обратную Связь Сложному Человеку. Продолжение

Начало

2. Делитесь наблюдениями, а не осуждайте
Человек, получающий обратную связь, может почувствовать, что вы осуждаете его, в течение первых нескольких минут, основываясь на вашем тоне и языке тела. Это заставляет его либо замолчать (полагая, что ничто сказанное не изменит вашего мнения), либо превратить разговор в спор, чтобы доказать, что он прав, а вы нет.

Керри Паттерсон говорит в книге «Ключевые переговоры»: «Уважение подобно воздуху. Пока оно есть, об этом никто не думает. Но если убрать его, это все, о чём люди смогут думать. В тот момент, когда люди чувствуют неуважение в разговоре, взаимодействие больше не имеет первоначальной цели — теперь речь идёт о защите достоинства».

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

Ваша цель не в том, чтобы заставить его чувствовать себя плохо, бросить вызов его поведению или способам работы — это только сделает его невосприимчивым ко всему, что вы говорите. Вместо этого сделайте следующее:
- Поделитесь наблюдениями. «Я заметил, я услышал, я наблюдал, я увидел, мне сказали» вместо «Ты сделал, ты сказал».
- Продолжайте обсуждение конкретной проблемы, а не человека.
- Придерживайтесь фактов.
- Говорите о влиянии на команду.
- Предоставьте человеку возможность найти решение.

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

Вместо: Вы кричите на других. Это не поможет. Научитесь контролировать свой характер.
Скажите: Мне сказали, что на последней встрече вы повысили голос (факт). Из-за этого другие в комнате не захотели высказываться. Без их вклада и согласия мы не сможем принять решение, что задержит реализацию проекта. Давайте попробуем поддерживать идеи других и побудить их делиться мыслями. (решение)

Вместо: Вы очень грубы и постоянно перебиваете других. Это неприемлемо.
Скажите: В последнем обсуждении я заметил, что, когда Рея делилась решением, вы несколько раз перебивали её и уводили разговор в сторону (факт). Когда вы не позволяете другим договорить, они чувствуют себя неуслышанными, что подрывает их уверенность. Мы можем расти как команда только тогда, когда каждый имеет возможность делиться своим мнением. Что вы можете сделать, чтобы этому поспособствовать? (решение)

Вместо: Ты сказал Карлу, что он тупой. Это непрофессионально.
Скажите: Мне сказали, что ты назвал дизайн Карла тупым (факт). Карл — очень преданный своему делу сотрудник, который всегда стремится учиться. Это нормально чего-то не знать. Это не делает человека тупым. Вместо того, чтобы разочаровывать людей, когда они чего-то не знают, нам нужно поощрять их учиться. Давайте решим, как вы можете работать с командой так, чтобы поощрять коллег, а не разочаровывать? (решение)

Окончание следует…

Источник:
https://www.techtello.com/give-feedback-to-a-difficult-person/
👍9
День 1747. #Карьера #ProjectManagement
Как Дать Эффективную Обратную Связь Сложному Человеку. Окончание

Начало
Продолжение

3. Слушайте, дайте человеку почувствовать себя услышанным
Доктор Ральф Николс, пионер изучения умения слушать, сказал: «Самая основная из всех человеческих потребностей — это потребность понимать и быть понятым. Лучший способ понять людей — это слушать их». Слушать не означает соглашаться с их точкой зрения, это просто даёт им понять, что вы их услышали.

Чувство того, что вас не слышат, вызывает негативные эмоции, которые омрачают способность ясно мыслить и участвовать в конструктивной дискуссии. Как только человек чувствует неодобрение, срабатывает защитная реакция, спасающая его от риска эмоционального воздействия: он отказывается брать на себя ответственность или перекладывает вину на кого-то другого.

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

Вот несколько дополнительных вопросов для взаимодействия и общения:
- Расскажите подробнее об этом.
- Что вы об этом думаете?
- Как бы вы это решили?
- Как вы думаете, почему это произошло?
- Какой может быть альтернативная точка зрения на это?
- Если бы вы оказались в такой ситуации, как бы вы себя почувствовали?

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

4. Установите дедлайн изменений
Джоко Виллинк говорит в книге «Экстремальная ответственность»: «Важно не то, что вы проповедуете, а то, что вы терпите».

Здоровые границы необходимы для психического благополучия всех сотрудников на работе. Когда эти границы нарушаются трудными людьми — либо потому, что не установлены соответствующие ожидания того, что представляет собой токсичность, либо потому, что у них просто проблемы с отношением к работе, не важно — чем больше времени кто-то проводит с ними, тем больше вреда они терпят.

Если позволить трудным людям оставаться в системе слишком долго, это подрывает усилия многих других. Эмоциональное истощение от пребывания рядом с трудными людьми влияет на то, как люди работают, что делают, и, наконец, на то, чего добивается коллектив в целом.

Когда вы даёте обратную связь сложному человеку, не говорите просто «о проблеме». Установите время на взаимно согласованные изменения и дайте понять, что человек должен быть готов столкнуться с последствиями, если не сможет учиться и совершенствоваться.

Для этого скажите: «Давайте убедимся, что мы друг друга поняли. Через [сколько-то времени] вы [излагаете ваши ожидания]. Я готов помочь вам, если вы захотите обсудить что-нибудь ещё. Но имейте в виду, что я не отношусь к этому легкомысленно, и, если ситуация не улучшится, будут последствия».

Итого
«Трудные» люди, особенно те, у которых к тому же все хорошо на работе, плохо воспринимают обратную связь. Чтобы разговор был эффективным:
1. Будьте аккуратны в выборе слов. Эмоционально заряженные слова могут вызвать негативные эмоции и заставить человека защищаться.
2. Исключите обратную связь, которая воспринимается как осуждение. Поделитесь наблюдениями, поговорите о влиянии на команду и предложите человеку найти решение.
3. Послушайте человека. Дайте ему возможность подумать, поразмышлять и найти собственное решение.
4. Не оставляйте отзыв без чётких ожиданий относительно желаемых изменений, сроков и последствий несерьёзного отношения к критике.

Источник: https://www.techtello.com/give-feedback-to-a-difficult-person/
👍6
День 1748. #ЧтоНовенького
Оператор Расширения для Коллекций в C# 12.

Выражения коллекций (также известные как литералы коллекций) — это новая функция C#12, которая добавляет в C# оператор расширения. Он позволяет объединить коллекции распространённых типов (массивы, диапазоны и списки) в одну. Кроме того, для типов коллекций изменён синтаксис, который упрощает код и помогает уменьшить его многословность.

Инициализация
В версиях до C#12 инициализация типа коллекции была довольно многословной, поскольку нам приходилось указывать тип, который мы инициализируем. Например, вот как инициализировать массив, диапазон и список целыми числами:

var a = new int[] { 1, 2, 3 };
var b = new Span<int>(new int[] { 2, 4, 5, 4, 4 });
var c = new List<int> { 4, 6, 6, 5 };

Новый синтаксис в C#12
Теперь мы можем использовать выражения коллекций. Мы объявляем переменную с типом коллекции, который хотим использовать, а затем заключаем значения в квадратные скобки:

int[] a = [1, 2, 3];
Span<int> b = [2, 4, 5, 4, 4];
List<int> c = [4, 6, 6, 5];

Это сокращает количество кода, и, как по мне, более читабельно выглядит.

Оператор расширения (spread)
Оператор расширения популярен в JavaScript для объединения и добавления значений к существующим типам коллекций. И наконец он появился в C#.

Чтобы использовать его, инициализируйте новый тип коллекции, а затем добавьте .. перед добавлением переменной типа коллекции.

int[] join = [..a, ..b, ..c];

Также можно добавлять дополнительные значения:

int[] join = [..a, ..b, ..c, 6, 5];

Не имеет значения, какие типы коллекций мы объединяем, а также не имеет значения, какой тип коллекции используется для переменной, имеющей оператор расширения. Мы можем использовать оператор расширения для объединения разных типов коллекций и объявлять переменную любым из этих типов коллекций:

int[] a = [1, 2, 3];
Span<int> b = [2, 4, 5, 4, 4];
List<int> c = [4, 6, 6, 5];

List<int> join = [..a, ..b, ..c, 6, 5];

Источник: https://www.roundthecode.com/dotnet-tutorials/collection-expressions-brings-spread-operator-csharp-12
👍51
День 1749.
.NET Conf 2023.
Вот опять ноябрь, а это значит, выпуск новой версии .NET и посвящённая этому .NET Conf 2023.

https://www.dotnetconf.net/

Вот краткое расписание первого дня по московскому времени (подробное расписание на сегодня и последующие дни тут https://www.dotnetconf.net/agenda).

14 ноября
19:00-20:00 Welcome to .NET 8 (Damian Edwards, Safia Abdalla, David Fowler, Gaurav Seth, Daniel Roth, Glenn Condron, Maddy Montaquila, Maria Naggaga)
20:00-21:00 Full stack web UI with Blazor in .NET 8 (Daniel Roth, Steve Sanderson)
21:00-22:00 Building Cloud Native apps with .NET 8 (Glenn Condron, David Fowler)
22:00-23:00 Performance Improvements in .NET 8, ASP.NET Core, and .NET MAUI (David Fowler, Stephen Toub, Jonathan Peppers)
23:00-23:45 What's New in C# 12 (Mads Torgersen, Dustin Campbell)
23:45-00:30 Packing light with VS Code and the C# Dev Kit (Burke Holland, Leslie Richardson)

15 ноября
00:30-01:15 Entity Framework Core 8: Improved JSON, queryable collections, and more… (Arthur Vickers, Shay Rojansky)
01:15-02:00 .NET 💖 AI (John Maeda, Scott Hanselman)
02:00-02:45 Build Intelligent Apps with .NET and Azure (Luis Quintanilla, Maria Naggaga)
02:45-03:30 What’s New in .NET MAUI (David Ortinau, Maddy Montaquila)
03:30-04:15 Building and scaling cloud-native, intelligent applications on Azure and .NET (Scott Hunter)
04:15-05:15 CodeParty Attendee Party (Jeffrey T. Fritz)

Смотрим!
👍13👎1
День 1750. #ЧтоНовенького
Топ 10 Новых Функций .NET 8. Начало

Итак, .NET 8 выпущен. Это LTS версия - с долгосрочной поддержкой в течение как минимум трех лет с момента запуска. Рассмотрим 10 лучших новых функций .NET 8.

1. Производительность
Как обычно, много внимания было уделено повышению производительности. Стивен Тауб написал огромную статью, в которой описываются все улучшения.

Вот наиболее заметные:
- Очень много улучшений связано с усовершенствованной оптимизацией на основе профиля (Dynamic PGO). Это метод оптимизации JIT-компилятора, который позволяет JIT собирать дополнительную информацию об окружении (так называемом профиле) в коде, чтобы полагаться на неё позже и «пересобирать» методы на «горячем пути», делая их ещё эффективнее. Так виртуальные вызовы методов могут заменяться прямыми, условные операторы инвертироваться, чтобы чаще переходить по «более горячему пути», могут клонироваться циклы и т.п.
- List<T>.AddRange(IEnumerable<T>) был переписан для повышения производительности, когда последовательность не является ICollection<T>. В этом случае AddRange не может использовать свойство Count и приходится итерировать всю добавляемую коллекцию. В .NET 8 этот метод работает быстрее на 60%, по сравнению с .NET 7.
- Int32.ToString() ускорен благодаря кэшированию значений от 0 до 299. В результате для этих чисел ToString() работает быстрее почти на 90% и ничего не аллоцирует, но и для других чисел он ускорен примерно на 40%.

2. Динамическая Адаптация к Размерам Приложений (DATAS)
В .NET 8 в сборщик мусора добавлена функция DATAS (Dynamic Adaptation To Application Sizes). Она регулирует использование памяти приложением на основе размера текущих данных (LDS), который включает в себя долгоживущие данные и текущие данные во время события сборки мусора. У DATAS есть два основных варианта использования:
- Для изменяющихся рабочих нагрузок в средах с ограниченной памятью, таких как контейнерные приложения с ограничениями памяти. DATAS уменьшает или увеличивает размер кучи по мере необходимости.
- Для небольших рабочих нагрузок с использованием Server GC - обеспечивает соответствие размера кучи фактическим требованиям приложения.
Основная задача DATAS — адаптация к размеру приложения. Сборщик мусора всегда динамически изменял параметры своей работы, но DATAS настраивает его так, чтобы он лучше соответствовал рабочим нагрузкам. См.
подробнее о DATAS.

3. Сериализация и десериализация в System.Text.Json
.NET 8 повышает удобство использования приложений Native AOT благодаря множеству улучшений в генераторах кода, включая:
- Поддержку сериализации типов с required и init свойствами.
- Атрибут JsonSourceGenerationOptions соответствующий JsonSerializerOptions, позволяющий настраивать сериализацию во время компиляции.
- Улучшенную обработку типов: игнорирует недоступные свойства, позволяет вкладывать объявления JsonSerializerContext и динамически обрабатывает типы, созданные компилятором.
- JsonStringEnumConverter<TEnum> новый тип преобразователя упрощающий сериализацию перечислений в нативных AOT-приложениях.
- Свойство JsonConverter.Type позволяющее получить тип неуниверсального экземпляра JsonConverter с поддержкой значений NULL для различных сценариев.

Множество других улучшений, в том числе:
- JsonNamingPolicy теперь включает новые политики именования для преобразования имён свойств в Snake_case и Kebab-Case.
- Десериализация данных в readonly поля или свойства, не имеющие сеттера.
- Отключение сериализатора на основе рефлексии по умолчанию. Эта возможность полезна для предотвращения непреднамеренного включения компонентов рефлексии, особенно в усеченных и нативных AOT-приложениях.
См. подробнее о новинках в System.Text.Json.

Продолжение следует…

Источник:
https://blog.ndepend.com/net-8-top-10-new-features/
👍29👎1
День 1751. #ЧтоНовенького
Топ 10 Новых Функций .NET 8. Продолжение

Начало

4. Работа с коллекциями
Теперь синтаксис квадратных скобок можно использовать для инициализации многих видов коллекций (а также для объединения коллекций), в том числе коллекций только для чтения и неизменяемых коллекций, которые нельзя инициализировать с помощью старого синтаксиса фигурных скобок:
// не компилируется
ImmutableList<int> list = { 1, 2, 3, 4 };

// работает
ImmutableList<int> list = [ 1, 2, 3, 4 ];

Также синтаксис [] можно использовать для инициализации пустой коллекции (в том числе словаря) вместо использования new List или Array.Empty.
К сожалению, к этому релизу не успели полноценно реализовать синтаксис инициализации словарей, вроде:
Dictionary<string, int> dict = ["a": 1, "b": 2];

Но он есть в предложениях на C# 13.

Кроме того, появились удобные методы для случайного перемешивания элементов коллекции. Например, новые методы System.Random.GetItems() и System.Security.Cryptography.RandomNumberGenerator.GetItems() позволяют случайным образом выбирать указанное количество элементов из входного набора:
ReadOnlySpan<int> numbers = 
[1, 2, 3, 4, 5, 6];
var diceRolls =
Random.Shared.GetItems(numbers, 5);
// 5, 1, 1, 4, 4

Новые методы Random.Shuffle и RandomNumberGenerator.Shuffle<T>(Span<T>) теперь позволяют рандомизировать элементы диапазона:
Span<int> numbers = [1, 2, 3, 4, 5, 6];
Random.Shared.Shuffle(numbers);
// 3, 4, 1, 5, 2, 6

5. Новые абстракции времени
Недавно представленный класс TimeProvider и интерфейс ITimer предлагают абстракции времени, облегчая моделирование времени в сценариях тестирования. TimeProvider — это абстрактный класс с множеством виртуальных функций, что делает его идеальным кандидатом для интеграции с фреймворками моков.

Заметьте, что вы также можете использовать абстракцию времени для имитации операций Task, которые зависят от хода времени, например Task.Delay() и Task.WaitAsync().
См. также
- Подробнее про класс TimeProvider.
- Большая статья Эндрю Лока

6. Форматирование UTF8
Чтобы включить генерацию строкового представления вашего типа в целевом спане, реализуйте недавно представленный интерфейс IUtf8SpanFormattable для вашего типа:
public interface IUtf8SpanFormattable {
bool TryFormat(Span<byte> utf8Destination, out int bytesWritten, ReadOnlySpan<char> format, IFormatProvider? provider);
}


Этот интерфейс похож на ISpanFormattable, но разработан специально для UTF-8 и Span<byte>, в отличие от UTF-16 и Span<char>. В .NET 8 все примитивные типы (и другие) реализуют этот интерфейс: Byte, Complex, Char, DateOnly, DateTime, DateTimeOffset, Decimal, Double, Guid, Half, IPAddress, IPNetwork, Int16, Int32, Int64, Int128, IntPtr, NFloat. , SByte, Single, Rune, TimeOnly, TimeSpan, UInt16, UInt32, UInt64, UInt128, UIntPtr и Version.

Разработан он в первую очередь для применения в minimal API и вебе (именно там мы чаще всего используем UTF-8). Вот пример:
Span<byte> dest = stackalloc byte[16];
int written = 0;
// 123.456
123.456123.TryFormat(dest, out written, ".###");
// 2021-11-16
DateTime.Now.TryFormat(dest, out written, "yyyy-MM-dd");

Console.WriteLine(
Encoding.UTF8.GetString(dest.Slice(0, written)));

Окончание следует…

Источник:
https://blog.ndepend.com/net-8-top-10-new-features/
👍15
День 1752. #ЧтоНовенького
Топ 10 Новых Функций .NET 8. Окончание

Начало
Продолжение

7. Потоковые методы ZipFile
Теперь можно сжимать файлы из каталога в поток без необходимости кэшировать их во временный файл. Это позволяет напрямую управлять результатом сжатия в памяти. Новый API полезен в сценариях, где дисковое пространство ограничено, поскольку устраняет необходимость использования диска в качестве промежуточного шага. Новые статические методы: ZipFile.CreateFromDirectory и ExtractToDirectory.

8. Новые типы, которые могут улучшить производительность в различных ситуациях
1) System.Collections.Frozen содержит новые классы коллекций FrozenSet<T> и FrozenDictionary<TKey,TValue>. Эти коллекции нельзя изменить после создания. Внутри реализация использует это требование, чтобы обеспечить более быстрое перечисление и более быстрые операции поиска, такие как Contains() или TryGetValue(). Эти коллекции оказываются особенно ценными в сценариях, где коллекции изначально заполняются и впоследствии сохраняются в течение всего жизненного цикла долгоживущего приложения.
См. подробнее о замороженных коллекциях

2) Класс System.Buffers.SearchValues<T> предназначен для эффективного поиска набора байтов или символов в другом наборе, например, в реализации String.IndexOfAny(char[]). При создании экземпляра SearchValues<T> все данные, необходимые для оптимизации будущего поиска, вычисляются заранее и выбирается наиболее эффективный алгоритм поиска. Класс уже широко используется внутри .NET, например, в кодогенерации для регулярных выражений.

3) Класс System.Text.CompositeFormat предназначен для оптимизации строк формата, таких как «Имя: {0} Фамилия: {1}», неизвестных во время компиляции. Он позволяет динамически создавать строки формата путем объединения строк и элементов формата (аналогично классу StringBuilder), а также предоставляет ряд функций для оптимизации строк формата, таких как кэширование и повторное использование. Несмотря на то, что такие задачи, как анализ строк, требуют начальных накладных расходов, этот упреждающий подход значительно снижает вычислительную нагрузку при последующих использованиях, повышая производительность и эффективность.

9. Поддержка набора инструкций Intel AVX-512.
В .NET Core 3.0 предложена поддержка SIMD за счёт включения аппаратной поддержки для платформы x86/x64. .NET 5 расширил эту поддержку до Arm64, в .NET 7 были представлены кроссплатформенные аппаратные функции. .NET 8 ещё расширяет возможности SIMD за счет введения Vector512<T> и поддержки инструкций Intel Advanced Vector Extensions 512 (AVX-512). Обратите внимание: если у вас есть оборудование, совместимое с AVX-512, Vector512.IsHardwareAccelerated теперь будет возвращать true.

10. Криптография
.NET 8 предлагает поддержку примитивов хеширования SHA-3. SHA-3 в настоящее время совместим с Linux и OpenSSL 1.1.1 или новее, а также с Windows 11 сборки 25324 или новее. Существующие API, предлагающие SHA-2, теперь включают свои аналоги SHA-3, включающие SHA3_256, SHA3_384 и SHA3_512 для хеширования; HashAlgorithmName.SHA3_256, HashAlgorithmName.SHA3_384 и HashAlgorithmName.SHA3_512 для хеширования, с настраиваемым алгоритмом; HMACSHA3_256, HMACSHA3_384 и HMACSHA3_512 для HMAC; и RSAEncryptionPadding.OaepSHA3_256, RSAEncryptionPadding.OaepSHA3_384 и RSAEncryptionPadding.OaepSHA3_512 для шифрования RSA OAEP.

Источник: https://blog.ndepend.com/net-8-top-10-new-features/
👍15
День 1753. #Оффтоп
Давненько не рекомендовал вам видосиков. Сегодня просто полный оффтоп, и не совсем про программирование. У АйтиБороды вышло шикарное интервью с Интервью с Михаилом Климарёвым, директором Общества Защиты Интернета.
Как зарождался и развивался Рунет, как его пытаются контролировать сейчас. А поскольку я в сети тоже уже очень давно, то знатно поностальгировал по временам диалапа и ADSL.

Так что запасайтесь 3,5 часами свободного времени https://youtu.be/eaz3vSihX0o

ЗЫ: если у кого аллергия на политоту, то её тут довольно много. Если что, я предупредил.
👎15👍11
День 1754. #Книги
Вчера пришла очередная книга, над переводом которой я работал совместно с сообществом DotNetRu. Крис Сейнти «Blazor в действии» — М.: ДМК Пресс, 2023.

Книгу у меня получилось не только вычитать, но и проверить код (кстати, исправил несколько косяков в оригинале). В принципе, тем, кто хочет познакомиться с технологией и понять, что там к чему и зачем, могу книгу порекомендовать. Единственная проблема, как и со всеми книгами по новым технологиям – книги сильно отстают, а новая технология быстро и кардинально меняется. Так и тут, после выхода .NET 8 различия между Blazor Server и Blazor WebAssembly практически нивелировались. Теперь при создании проекта не обязательно выбирать одно или другое, их можно использовать совместно. Однако, это не значит, что книга совсем бесполезна, вполне можно изучить основы по ней, а новинки посмотреть в документации (тем более, что эти нововведения только-только появились и пока ещё нет большой практики их применения).

Подробнее про книгу в статье на Хабре
👍33
День 1755. #ЧтоНовенького
Упрощаем Облачную Разработку с .NET Aspire

.NET Aspire — это оркестратор для создания отказоустойчивых, наблюдаемых и настраиваемых облачных приложений в .NET. Он включает набор компонентов для обнаружения сервисов, телеметрии, поддержки устойчивости и проверки работоспособности.

Новый шаблон .NET Aspire Starter доступен в Visual Studio 2022 (17.9 Preview 1), либо из командной строки. Приложение шаблона состоит из двух проектов (веб-интерфейс Blazor и API) и кэша Redis. Также включены два новых проекта:
1. <appname>.AppHost запускает .NET-проекты, контейнеры или исполняемые файлы, необходимые для вашего распределённого приложения.
2. <appname>.ServiceDefaults содержит общую сервисную логику, которая применяется к каждому из проектов в приложении: обнаружение сервисов, телеметрия и конечные точки проверки работоспособности.

Панель мониторинга
Запуск приложения .NET Aspire отображает панель мониторинга. Она даёт единое представление о ваших сервисах с их журналами, метриками, трассировками и ошибками. На картинке 1 ниже показано изображение проекта с ошибкой, обозначенной красной точкой. Также можно смотреть журналы всех проектов и распределённую трассировку, показывающую запрос на страницу погоды (см. картинку 2 ниже). На панели собираются все диагностические данные во время разработки. Используются открытые стандарты, такие как Grafana+Prometheus, Application Insights и т. д.

Компоненты
В веб-проекте приложения есть NuGet-пакет Aspire.StackExchange.Redis.OutputCaching. Такие пакеты называются компонентами .NET Aspire. Это связующие библиотеки, которые настраивают SDK для работы в облачной среде. Каждый компонент должен:
- Предоставлять схему JSON для конфигурации.
- Использовать настраиваемые шаблоны устойчивости, такие как повторные попытки, тайм-ауты и автоматические выключатели, чтобы максимизировать доступность.
- Предоставлять проверки работоспособности.
- Предлагать интегрированное ведение журнала, метрики и трассировку с использованием современных абстракций .NET (ILogger, Meter, Activity).
- Предлагать методы расширения, которые «склеивают» сервисы из SDK в DI-контейнер с правильным временем жизни для регистрируемых типов.

То есть компоненты .NET Aspire настраивают зависимости для соблюдения ряда требований для успешной работы в облаке. Они не оборачивают и не скрывают фактический SDK/библиотеку, а действуют как связующее звено, гарантируя, что библиотека настроена с хорошим набором значений по умолчанию и правильно зарегистрирована в DI.

Код
В Program.cs веб-проекта вы можете увидеть код:
builder.Services
.AddHttpClient<WeatherApiClient>(
client => client
.BaseAddress = new("https://apiservice"));

Это настройка веб-интерфейса для вызова API погоды. Название apiservice взято из проекта AppHost:
var builder = 
DistributedApplication.CreateBuilder(args);

var cache =
builder.AddRedisContainer("cache");

var apiservice = builder
.AddProject<Projects.AspireApp_ApiService>(
"apiservice");

Builder
.AddProject<Projects.AspireApp_Web>("webfrontend")
.WithReference(cache)
.WithReference(apiservice);

builder.Build().Run();

AppHost —стартовый проект. Он запускает все проекты, их зависимости и настраивает их, позволяя им взаимодействовать. Механизм обнаружения сервисов позволяет использовать логические имена вместо IP-адресов и портов при выполнении HTTP-вызовов. Здесь API-сервис назван "apiservice", и это имя можно использовать при выполнении HTTP-вызовов через IHttpClientFactory. Аналогично кэш Redis назван "cache". Вызовы, выполненные с использованием этого метода, также будут автоматически повторяться и обрабатывать временные сбои благодаря интеграции с Polly.

Подробнее о .NET Aspire смотрите в:
- докладе с .NET Conf
- видео от Ника Чапсаса
- деплой в Azure от Ника Чапсаса

Источник: https://devblogs.microsoft.com/dotnet/introducing-dotnet-aspire-simplifying-cloud-native-development-with-dotnet-8/
👍12
День 1756. #Testing
Тестируем Валидацию Модели

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

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

Определим простую модель:
public class User
{
[Required]
[MinLength(3)]
public string FirstName { get; set; }

[Required]
[MinLength(3)]
public string LastName { get; set; }

[Range(18, 100)]
public int Age { get; set; }
}

У нас есть два варианта: мы можем написать интеграционные тесты для отправки запросов в систему, на которой работает сервер, и проверить полученный ответ. Или мы можем использовать внутренний класс Validator, который используется ASP.NET для проверки моделей ввода, и создать быстрые юнит-тесты. Вот вспомогательный метод, который мы можем использовать в тестах:
public static IList<ValidationResult> 
ValidateModel(object model)
{
var results = new List<ValidationResult>();
var context =
new ValidationContext(model, null, null);

Validator.TryValidateObject(
model, context, results, true);

if (model is IValidatableObject vm)
results.AddRange(vm.Validate(context));

return results;
}

Мы создаём контекст проверки без какой-либо внешней зависимости, ориентированный только на модель ввода. Затем мы проверяем все свойства, вызывая TryValidateObject, и сохраняем результаты проверки в списке results. Наконец, если модель реализует интерфейс IValidatableObject, который предоставляет метод Validate, мы вызываем его и добавляем возвращённые ошибки в тот же список results. Таким образом мы можем обрабатывать как атрибуты полей, такие как [Required], так и пользовательскую проверку в методе Validate() класса модели.

Используем этот метод в тестах:
[Test]
public void Pass_WhenModelValid()
{
var model = new User {
FirstName = "Jon",
LastName = "Smith",
Age = 42
};

var result = ValidateModel(model);

Assert.That(result, Is.Empty);
}

[Test]
public void Fail_WhenAgeLessThan18()
{
var model = new User {
FirstName = "Jane",
LastName = "Smith",
Age = 17
};

var result = ValidateModel(model);

Assert.That(result, Is.Not.Empty);
}

Аналогично мы можем проверять и правильность возвращаемых ошибок, которые можно кастомизировать как в атрибутах валидации (свойство ErrorMessage), так и в методе Validate().

Источник: https://www.code4it.dev/csharptips/unit-test-model-validation/
👍12
День 1757. #ЗаметкиНаПолях
Версионирование API. Начало

Версионирование используется для управления изменениями в API при сохранении обратной совместимости для существующих клиентов. Это позволяет разработчикам внедрять новые функции, исправлять ошибки или вносить другие изменения в API, не влияя на функциональность существующих интеграций.

Причины:
- Обратная совместимость: гарантирует, что клиенты смогут продолжать использовать старую версию, в то время как новые клиенты смогут воспользоваться преимуществами обновлённых функций.
- Эволюция API: позволяет внедрять новые функции, отказываться от устаревших функций и вносить улучшения, не нарушая работу существующих клиентов.
- Гибкость для клиента: у разных клиентов могут быть разные требования или требоваться определённые функции, доступные в конкретной версии API.

Начиная с .NET 5, есть 5 вариантов версионирования API.

1. По URI (Пути)
Версия включается непосредственно в URL (/api/v1/products).
+ Наиболее простой и понятный метод.
- Может привести к дублированию URL и потребует от клиентов изменять URL для доступа к различным версиям.
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/products")]
public class ProductsV1Controller : ApiController
{
public IHttpActionResult Get()
{
// Реализация для версии 1
}
}

[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/products")]
public class ProductsV2Controller : ApiController
{
public IHttpActionResult Get()
{
// Реализация для версии 2
}
}


2. По строке запроса
Версия указывается в качестве параметра запроса в URL (/api/products?version=1).
+ Позволяет легко перейти на новую версию.
- Может быть не так очевидно, как управление через путь, и пользователи могут не заметить параметра.
public class ProductsController : ApiController
{
const string DefaultVersion = "1.0";

[HttpGet]
public IHttpActionResult Get()
{
var version = HttpContext.Current
.Request.QueryString["version"]
?? DefaultVersion;

if (version == "1.0")
{
// Реализация для версии 1
return Ok(…);
}
if (version == "2.0")
{
// Реализация для версии 2
return Ok(…);
}

return BadRequest("Версия не поддерживается");
}
}


Окончание следует…

Источник:
https://stefandjokic.tech/blog
👍17
День 1758. #ЗаметкиНаПолях
Версионирование API. Окончание
Начало

3. По HTTP-заголовку
Информация о версии отправляется в виде пользовательского заголовка в HTTP-запросе (X-API-Version: 1).
+ URL остаётся неизменным для разных версий, что может быть полезно для SEO и кэширования.
- Версию труднее обнаружить и сложнее тестировать, поскольку требуется установка HTTP-заголовков.
public class ProductsController : ApiController
{
private const string HeaderName = "X-API-Version";

[HttpGet]
public IHttpActionResult Get()
{
var version = HttpContext.Current
.Request.Headers[HeaderName];

if (version == "1.0")
{
// Реализация для версии 1
return Ok(…);
}
if (version == "2.0")
{
// Реализация для версии 2
return Ok(…);
}

return BadRequest("Требуется версия API");
}
}


4. По Media Type (заголовок Accept)
Информация о версии включается в заголовок Accept HTTP-запроса, часто с использованием пользовательского типа (application/vnd.myapi.v1+json).
+ Метод точно соответствует спецификации HTTP.
- Более сложно, а некоторым клиентам API может быть сложно управлять версиями.
[Produces("application/json")]
public class ProductsController : ApiController
{
[HttpGet]
public IHttpActionResult Get()
{
var version = GetVersionFromAcceptHeader();
// реализация аналогична методу 3
}

private string GetVersionFromAcceptHeader()
{
var header = Request.Headers.
Accept.FirstOrDefault();
if (header != null)
{
var version =
header.Parameters.FirstOrDefault(
p => p.Name.Equals("version",
StringComparison.OrdinalIgnoreCase));
return version?.Value;
}
return null;
}
}


5. По имени хоста
Разные версии API размещаются на разных доменных именах (api-v1.example.com). Может осуществляться через маршрутизацию или перезапись URL. Обычно не обрабатывается непосредственно в контроллере, а скорее в настройках IIS или обратного прокси.
+ Наиболее интуитивно понятен для конечных пользователей.
- Требует дополнительной настройки инфраструктуры и может усложнить управление сертификатами SSL.

Итого
У каждого метода есть свои компромиссы, и выбор того, какой из них использовать, будет зависеть от конкретных требований и ограничений проекта. Также можно комбинировать эти методы. Конечная цель в том, чтобы поддерживать надёжный контракт с потребителями API, обеспечивая при этом возможность дальнейшего развития и улучшения сервисов, предоставляемых через API.

Источник: https://stefandjokic.tech/blog
👍11
День 1759. #Оффтоп #Безопасность
Осторожно: URL – Это Указатели на Изменяемые Сущности

Люди часто оценивают URL как: «Безопасные или вредоносные». В частности, поставщики продуктов безопасности ставят URL в один ряд с такими индикаторами опасности, как хэш известного вредоносного файла, вредоносный/скомпрометированный цифровой сертификат или заведомо вредоносный IP.

К сожалению, это разные вещи. Хэш файла никогда не меняется, содержимое файла не может измениться без изменения его хэша, и «безвредный» файл не может стать «вредоносным»*.
*Мы говорим о самом файле, а не о пути к файлу.

Однако IP или URL, может легко измениться с «безобидного» на «опасный» и наоборот. IP – просто указатель, а URL — указатель на указатель. И даже после того, как клиент подключается к целевому серверу, этот сервер решает, как интерпретировать запрос, и может каждый раз возвращать разное содержимое.

Поэтому не только URL со временем может измениться с безобидного на вредоносный (например, злоумышленник приобретёт домен после истечения срока его регистрации). URL может даже быть одновременно безвредным и вредоносным в зависимости от того, кто его запрашивает (например, злоумышленник может «замаскировать» свою атаку, чтобы вернуть вредоносный контент целевым жертвам, одновременно предоставляя безвредный контент другим). Или сервер может выдавать разные ответы разным клиентам.

Недавно поиск «YouTube» в Google приводил к тому, что первая ссылка на странице была «рекламной» ссылкой. Если ничего не подозревающий пользователь нажимал на ссылку, он попадал на сайт мошенников, которые пытались захватить экран и убедить пользователя, что ему нужно позвонить злоумышленнику.

Как это возможно? Злоумышленники просто воспользовались тем фактом, что URL являются указателями на изменяемые объекты. Скорее всего злоумышленник разместил заказ на рекламу и указал ссылку на редиректор, который перенаправлял на какую-то страницу на YouTube. Система проверки рекламы Google проверила целевой URL, чтобы убедиться, что он действительно указывает на YouTube, и начала показывать рекламу. Затем злоумышленник обновил редиректор на свой мошеннический сайт — классическая уязвимость «время проверки, время использования».

Цепочка перенаправлений браузера при нажатии ссылки
Из-за того, как работает модель безопасности веб-платформы, способность Google обнаруживать такого рода уловки ограничена: после того, как браузер пользователя покидает сервер googleadservices.com, рекламная система Google не знает, где в итоге окажется пользователь, и не может знать, что следующий редиректор теперь отправляет пользователя на сайт атаки.

Более того, вы можете подсмотреть на подсказку статуса браузера в нижнем углу браузера, чтобы увидеть, куда ведёт ссылка, прежде чем нажать на неё. Но на самом деле перед этим есть ещё один уровень косвенности: ссылка (элемент <a>) сама по себе является указателем-на-указатель-на-указатель. Благодаря манипуляциям с JavaScript URL, на который указывает ссылка на странице, может измениться при нажатии на него.

В частности, страница результатов поиска Google помещает URL в тэг <a>, но, когда пользователь нажимает на него, URL изменяется на «настоящий»: googleadservices.com/... и только потом осуществляется перенаправление на целевой URL.

Вредоносные сайты всегда могли подделать статус, но производители браузеров ожидали, что «хорошие сайты» этого не делают. К сожалению, это неверное предположение. В этом случае отображение «настоящего» URL в статусе, вероятно, не добавит никакой защиты для осторожного пользователя — все ссылки на странице результатов Google проходят через какой-то редиректор.

Источник: https://textslashplain.com/2023/10/13/beware-urls-are-pointers-to-mutable-entities/
👍10
День 1760. #ЗаметкиНаПолях
Меняем Поведение Конструкторов Копирования Записей
Когда вы используете записи, вы можете создать новый экземпляр, используя ключевое слово new или скопировать экземпляр с некоторыми изменениями, используя выражение with. Выражение with копирует все поля из исходного экземпляра, а затем применяет изменения.
var john = new Person("John", "Doe");
var Jane = john with { FirstName = "Jane" };

record Person(string FirstName, string LastName);

Если запись содержит изменяемый объект, например List<T>, экземпляр будет совместно использоваться оригиналом и копией. Это связано с тем, что выражение with не клонирует изменяемый объект:
var john = new Person("John", "Doe") { 
Phones = ["1234567890"]
};
var jane = john with { FirstName = "Jane" };
jane.Phones.Add("123");

// Both list are modified
Console.WriteLine(john.Phones.Count); // 2
Console.WriteLine(jane.Phones.Count); // 2

public record Person(string FirstName, string LastName)
{
public List<string>? Phones { get; init; }
}

При использовании выражения with вызывается созданный компилятором метод <Clone>$. Он вызывает конструктор копирования. Если вы не реализуете конструктор копирования, компилятор сгенерирует конструктор, копирующий все поля. Вот сгенерированный код для записи Person:
internal class Person
{
// ...

[CompilerGenerated]
public virtual Person <Clone>$()
{
return new Person(this);
}

[CompilerGenerated]
protected Person(Person original)
{
<FirstName>k__BackingField = original.<FirstName>k__BackingField;
<LastName>k__BackingField = original.<LastName>k__BackingField;
<Phones>k__BackingField = original.<Phones>k__BackingField;
}
}

Если вы хотите клонировать свойство Phones при использовании выражения with, вы можете реализовать конструктор копирования вручную:
public record Person(string FirstName, string LastName)
{
public List<string>? Phones { get; init; }

protected Person(Person other)
{
FirstName = other.FirstName;
LastName = other.LastName;
if(other.Phones != null)
Phones = new List<string>(other.Phones);
}
}

var john = new Person("John", "Doe") {
Phones = ["1234567890"];
var jane = john with { FirstName = "Jane" };
jane.Phones.Add("0987654321");

// Изменится только оригинал, но не копия
Console.WriteLine(john.Phones.Count); // 1
Console.WriteLine(jane.Phones.Count); // 2


Источник: https://www.meziantou.net/customizing-the-behavior-of-the-record-copy-constructor.htm
👍23👎2