.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
.NET Разработчик pinned «Вы пользуетесь .NET MAUI?»
День 1974. #ЗаметкиНаПолях
Согласованные и Воспроизводимые Сборки с Помощью global.json. Начало

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

Под давлением сроков Том и Брайан не тратят время на то, чтобы выяснить, почему ошибка не появляется на машине Тома. Они решают отключить ошибку с помощью директивы #pragma, и PR успешно создаётся. Через несколько минут приложение успешно развёртывается в рабочей среде.

Вроде бы всё кончается хорошо, но это не так. Если бы Том и Брайан потратили время на более внимательное изучение ситуации, они бы обнаружили несколько нарушений в процессе сборки приложения. Вот что произошло на самом деле.

Том использует версию 8.0.200 SDK для .NET, а Брайан — версию 8.0.204. Заинтересовавшись новыми возможностями .NET 9, Брайан также установил предварительную версию 9.0.100-preview.4. Конвейер CI, размещенный в Azure DevOps, использует задачу UseDotNet@2 для установки версии SDK 8.x, включая предварительные версии.

Ошибка IDE0100 появилась в тот день, когда Microsoft выпустили SDK версии 8.0.300. В этой версии представлено исправление, изменяющее поведение анализатора Roslyn IDE0100. Том, использующий более раннюю и уязвимую версию .NET 8 SDK, не пострадал. Однако Брайан скомпилировал приложение с помощью SDK предварительной версии .NET 9, которая включала это новое поведение.

Ирония в том, что приложение развёртывается в контейнере, а в качестве базового образа используется mcr.microsoft.com/dotnet/sdk:8.0.100, первой версии пакета SDK для .NET 8, выпущенной в ноябре 2023 года. Эта версия содержит несколько известных уязвимостей и больше не отображается в Docker Hub.

Какие выводы можно сделать из этой истории, вдохновлённой реальными событиями:
1. Команда Тома и Брайана не реализовала механизм, гарантирующий, что версия .NET SDK, используемая для компиляции приложения, одинакова на всех машинах.
2. Их поведение CI может измениться без предварительного уведомления, поскольку Microsoft выпускает новые предварительные версии SDK.
3. Версии SDK, содержащие уже исправленные уязвимости, по-прежнему используются как на машинах разработчиков, так и в производстве.

Этой ситуации можно избежать, явно указав необходимую версию .NET SDK для компиляции приложения. В продолжении рассмотрим, чем отличаются версии SDK и как использовать необходимую версию с помощью файла global.json.

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

Источник:
https://anthonysimmon.com/automate-dotnet-sdk-updates-global-json-renovate/
👍22
День 1975. #ЗаметкиНаПолях
Согласованные и Воспроизводимые Сборки с Помощью global.json. Продолжение

Начало

Понимание версий .NET SDK
Ежегодно выпускается основная версия .NET: 5.0, 6.0, 7.0, 8.0 и скоро 9.0 в ноябре 2024 года. Обычно именно на эту версию разработчики ссылаются в проектах
<TargetFramework>netX.0</TargetFramework>.

Однако пакет SDK для .NET обычно получает ежемесячные обновления, которые могут включать исправления безопасности, новые функции или обновления компонентов, таких как NuGet или MSBuild. Поэтому управление версиями SDK немного отличается от управления версиями среды выполнения. Например, первым SDK для .NET 8 была версия 8.0.100. Эта версия соответствует функциональному диапазону 8.0.1nn. Увеличение цифры сотых в третьем разделе номера версии может указывать на добавление новых функций и, возможно, на критические изменения.

Таким образом, 8.0.101 и 8.0.201 относятся к разным функциональным группам, а 8.0.101 и 8.0.199 — к одной функциональной группе. Изменение цифр nn в 8.0.1nn указывает на то, что новых функций нет, и чаще всего это соответствует исправлениям уязвимостей или ошибок.

Формат версии .NET SDK выглядит так: x.y.znn
Здесь:
- x — основная версия, обычно увеличивающаяся с каждым ежегодным основным выпуском.
- y — это дополнительная версия, которая остаётся 0, начиная с .NET 5.
- z — диапазон функций, который может меняться ежемесячно, указывая на добавление новых функций.
- nn — версия исправления, которая может меняться ежемесячно, указывая на исправления ошибок или уязвимостей.

Файл global.json
Файл global.json позволяет определить, какая версия .NET SDK используется для запуска команд .NET CLI, таких как dotnet build, dotnet run или dotnet test. При отсутствии этого файла используется последняя версия SDK, установленная на машине. В большинстве случаев файл создаётся в корне решения:
{
"sdk": {
"version": "8.0.300",
"rollForward": "latestPatch",
"allowPrerelease": false
}
}

sdk.version - минимальная версия SDK, необходимая в соответствии со стратегией rollForward.

sdk.rollForward - можно ли использовать версию выше, чем sdk.version. Некоторые возможные значения:
- latestPatch (по умолчанию) - допускает использование любой более высокой версии исправления, например, 8.0.300 разрешает 8.0.301, 8.0.399 и т.п.
- latestFeature - позволяет использовать любую более высокую функциональность. 8.0.300 разрешает 8.0.400, 8.0.599 и т.п.
- latestMinor - то же для второстепенной версии. 8.0.300 разрешает 8.1.100, 8.2.199 и т.п.
- latestMajor - допускает любую более старшую основную версию.
- disable указывает, что требуется точная версия.

Рекомендации
- Используйте последнюю доступную версию SDK, которую вы используете. Его можно получить на странице загрузки .NET SDK.
- Разрешите только исправления, чтобы избежать критических изменений.
- Запретите предварительные версии (allowPrerelease = false).

Цель состоит в том, чтобы обеспечить воспроизводимые сборки и идентичное поведение от машины разработчика до производства.

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

Источник:
https://anthonysimmon.com/automate-dotnet-sdk-updates-global-json-renovate/
👍11
День 1976. #ЗаметкиНаПолях
Согласованные и Воспроизводимые Сборки с Помощью global.json. Окончание

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

Настройка CI для использования файла global.json.
Azure DevOps и GitHub Actions позволяют установить пакет SDK для .NET в конвейере CI. Эти задачи можно настроить на использование файла global.json и гарантировать, что версия SDK совпадает с версией компьютера разработчика.
Azure DevOps:
- task: UseDotNet@2
displayName: "Install .NET SDK from global.json"
inputs:
packageType: "sdk"
useGlobalJson: true

GitHub Actions:
 
# Задача попытается найти файл global.json в текущем каталоге
- uses: actions/setup-dotnet@v4

# Также можно указать путь
- uses: actions/setup-dotnet@v4
with:
global-json-file: "./something/global.json"


Обновление версий образов Docker
Давайте посмотрим на эти несколько тегов образов Docker для .NET SDK, среды выполнения .NET и ASP.NET Core:
mcr.microsoft.com/dotnet/sdk:8.0
mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim
mcr.microsoft.com/dotnet/sdk:8.0-alpine
mcr.microsoft.com/dotnet/runtime:8.0
mcr.microsoft.com/dotnet/runtime:8.0-jammy-chiseled
mcr.microsoft.com/dotnet/aspnet:8.0-jammy

Их объединяет то, что они указывают не конкретную версию, а «последнюю версию ветки 8.0». Использование этих тегов может иметь несколько последствий:
- Если вы забудете загрузить последний образ, это может привести к использованию старой версии.
- Может быть выпущена новая функциональная группа, возможно, с критическими изменениями.
- Для ясности и согласованности с файлом global.json желательно указать точную версию .NET SDK. Например, mcr.microsoft.com/dotnet/sdk:8.0.301:
# Сборка приложения
FROM mcr.microsoft.com/dotnet/sdk:8.0.301 AS build
# [...]

# Сборка образа среды выполнения
FROM mcr.microsoft.com/dotnet/aspnet:8.0.3
# [...]


Автоматизация обновления версий .NET SDK
На этом этапе вы можете быть уверены, что разработчики, CI и контейнеры используют одну и ту же версию .NET SDK. Результат сборки, который вы получите, везде будет одинаковым.

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

Для этого вы можете использовать инструменты управления зависимостями, такие как Renovate или Dependabot, чтобы каждая новая версия .NET SDK автоматически создавала пул-реквест для обновления файла global.json и Dockerfiles.

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

Источник: https://anthonysimmon.com/automate-dotnet-sdk-updates-global-json-renovate/
👍13
День 1977. #УрокиРазработки
Уроки 50 Лет Разработки ПО

Урок 13. Две распространённые практики выявления требований — телепатия и ясновидение. Обе не работают

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

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

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

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

Например, требование: «Система должна поддерживать...» Как разработчики компании-подрядчика узнают, какую именно функциональность подразумевает слово «поддерживать»?

Или предлагается предусмотреть возможность отмены некоторых операций. Разработчик реализует её, она работает. Но затем разработчика спрашивают, как выполнить повтор операции? Её не просили реализовывать, но пользователь, привыкший видеть отмену/повтор в других приложениях, подразумевал, что они реализуются в паре. Разработчик добавляет функцию повтора, и она работает. Но потом пользователь удивляется, почему повтор применяется только к последней отменённой операции. И так далее.

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

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

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

Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 2.
👍12
День 1978. #Курсы
Изучаем .NET Aspire

Сегодня порекомендую вам обучающее видео (точнее запись недавней трансляции) с канала dotnet. Джефф Фритц рассказывает о .NET Aspire. Если вы не знаете, что это и с чем его едят, вот отличное 2х-часовое руководство по его практическому использованию.

https://youtu.be/8i3FaHChh20
👍4
В каком порядке выполняется промежуточное ПО в конвейере ASP.NET Core при обработке запроса?
#Quiz #ASPNET
Anonymous Quiz
76%
в порядке регистрации
7%
в порядке обратном регистрации
2%
в произвольном порядке
15%
в зависимости от типа промежуточного ПО
👍1
День 1979. #ЧтоНовенького
Новинки
ASP.NET Core 9 Preview 5
Недавно Microsoft выпустили 5й превью .NET 9, добавив значительные улучшения в ASP.NET Core.

1. MapStaticAssets
Основным улучшением в этом выпуске является оптимизация доставки статических веб-ресурсов. Новый API MapStaticAssets предназначен для замены UseStaticFiles в большинстве случаев (кроме обслуживания внешних ресурсов). MapStaticAssets оптимизирован для ресурсов, известных на момент сборки и публикации. Он сжимает ресурсы с помощью gzip и brotli, уменьшая их размер и ускоряя время загрузки для пользователей.
MapStaticAssets также настраивает заголовки ETag на основе содержимого, гарантируя, что браузер загружает файлы только в случае изменения их содержимого. Это упрощает процесс для разработчиков, поскольку новые библиотеки или ресурсы JS/CSS автоматически оптимизируются и обслуживаются быстрее, что особенно полезно для мобильных пользователей с ограниченной пропускной способностью.
Утверждается, что даже приложения, использующие сжатие на стороне сервера, могут извлечь выгоду из MapStaticAssets, поскольку он допускает более высокие степени сжатия, замедляя при этом процесс сборки. Однако использование сжатия Brotli может уменьшить, например, bootstrap.min.css со 163КБ до 17,5КБ.

2. Повторные подключения к серверу в Blazor
Для работы серверных приложений Blazor требуется подключение в режиме реального времени. Новые изменения вводят экспоненциальную стратегию отсрочки попыток повторного подключения:
- Когда пользователь возвращается к приложению с отключенным каналом, попытка повторного подключения выполняется немедленно. Это улучшает взаимодействие с пользователем при возвращении к приложению на вкладке браузера, которая перешла в спящий режим.
- Если попытка повторного подключения достигает сервера, но сервер уже разорвал канал, обновление страницы происходит автоматически. Это избавляет пользователя от необходимости вручную обновлять страницу.
- По умолчанию первые несколько попыток повторного подключения происходят быстро друг за другом, прежде чем между попытками вводятся задержки. Размер задержек можно настраивать.
- Также улучшен UI повторных подключений.

3. Определение режима рендеринга компонента во время выполнения
Класс ComponentBase теперь включает свойство Platform, которое вскоре будет переименовано в RendererInfo, со свойствами Name и IsInteractive. Эти свойства помогают разработчикам понять, где работает их компонент и является ли он интерактивным. Новое свойство AssignedRenderMode также предоставляет информацию о том, как компонент будет отображаться после пререндеринга.

4. Упрощена сериализация состояния аутентификации в Blazor
Если вы начали с шаблона проекта веб-приложения Blazor и выбрали параметр «Индивидуальные учетные записи», всё работает хорошо. Но требуется написать много кода, если вы пытаетесь добавить аутентификацию в существующий проект. Новые API, AddAuthenticationStateSerialization и AddAuthenticationStateDeserialization, упрощают этот процесс, добавляя необходимые сервисы для сериализации и десериализации состояния аутентификации и расширяя возможности приложения для доступа к состоянию аутентификации.

5. Шаблон приложения .NET MAUI Blazor
Этот шаблон упрощает создание приложений, предназначенных для Android, iOS, Mac, Windows и Интернета, одновременно обеспечивая максимальное повторное использование кода.
Ключевые особенности шаблона:
- Возможность выбрать режим интерактивного рендеринга Blazor для веб-приложения.
- Автоматическое создание соответствующих проектов, включая веб-приложение Blazor и гибридное приложение .NET MAUI Blazor.
- Созданные проекты используют общую библиотеку классов Razor (RCL) для поддержки компонентов пользовательского интерфейса Razor.
- Включен пример кода, который демонстрирует, как использовать внедрение зависимостей для предоставления различных реализаций интерфейса для гибридного приложения Blazor и веб-приложения Blazor.

Источники:
-
https://www.infoq.com/news/2024/06/asp-net-core-9-preview5/
-
https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-9.0
👍8
День 1980. #ЗаметкиНаПолях
ReadOnly-Коллекция не Является Неизменяемой

Коллекция «только для чтения» не означает, что она не изменяема. Сегодня посмотрим, как можно изменить ReadOnlyCollection.

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

ReadOnlyCollection не является неизменяемым представлением, это, по сути, просто представление коллекции или списка. Тем не менее, если исходный список изменится, изменится и ReadOnlyCollection:
var numbers = new List<int> { 1, 2 };
var asReadOnly = numbers.AsReadOnly();
var readOnly = new ReadOnlyCollection<int>(numbers);

Console.WriteLine($"List: {numbers.Count}");
Console.WriteLine($"AsReadOnly: {asReadOnly.Count}");
Console.WriteLine($"ReadOnly: {readOnly.Count}");

Вывод довольно очевиден:
List: 2
AsReadOnly: 2
ReadOnly: 2

Но что, если мы добавим элемент в исходный список?
numbers.Add(3);

Получим:
List: 3
AsReadOnly: 3
ReadOnly: 3

Как и представление в базе данных, наше представление списка «только для чтения» обновляется. Это плохо? Нет, потому что имеет некоторые преимущества. Самое главное - базовая операция по созданию ReadOnlyCollection очень дёшева. Она занимает O(1) времени, поскольку аллокации памяти не требуется.

Если мы хотим иметь реальную неизменяемость, нужно использовать другие типы, например, ImmutableList. Если мы проведём тот же тест, что и выше, мы получим то, что ожидаем от неизменяемого типа:
var immutable = numbers.ToImmutableList();
Console.WriteLine($"List: {numbers.Count}");
Console.WriteLine($"Immutable: {immutable.Count}");

numbers.Add(4);

Console.WriteLine($"List: {numbers.Count}");
Console.WriteLine($"Immutable: {immutable.Count}");

Вывод:
List: 3
Immutable: 3
List: 4
Immutable: 3

Мы видим, что длина остаётся прежней. Недостаток очевиден: нам нужно скопировать все элементы из исходного списка в неизменяемый список за O(n).

Итого
ReadOnly-коллекции не являются неизменяемыми. Это просто представление коллекции, доступное только для чтения. Вы как потребитель не можете их изменить, но это не значит, что изначальный создатель/владелец списка не может этого сделать. Если вам действительно нужно текущее состояние, которое не может измениться, используйте ImmutableArray/ImmutableList или Frozen-коллекции.

Источник: https://steven-giesel.com/blogPost/c20eb758-a611-4f98-9ddf-b9e2b83fcac9/readonlycollection-is-not-an-immutable-collection
👍38
День 1981. #ProjectManagement
Настоящий 10х-Разработчик Делает Всю Команду Лучше

Все знакомы с концепцией 10х-инженера: «занудного, асоциального гения, который создаёт новаторские продукты почти случайно». Суперразработчик, который в десять раз умнее и продуктивнее своих коллег, о котором ходят мифы и на которого идут венчурные инвесторы.

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

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

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

Это требует времени и энергии сотрудников. Не отвлекает ли это их от основных должностных обязанностей?

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

Вот некоторые конкретные способы, с помощью которых разработчики (и их менеджеры) могут построить культуру общего обучения:
- Если вы менеджер или старший сотрудник, подавайте пример, уделяя приоритетное внимание собственному обучению. Как бы вы ни были заняты, старайтесь уделять регулярное время изучению и отработке новых навыков. Сообщайте подчинённым, что вы делали и что вы узнали, чтобы они осознали, что постоянное обучение приветствуется в организации.
- Убедитесь, что сотрудники уделяют время обучению. Предоставьте командам возможность выделять время для обучения. Иначе им придётся посвящать личное время обучению, а не делать обучение неотъемлемой частью вашей организационной культуры и повседневной работы.
- Делитесь ошибками с коллегами. Любая ошибка — это возможность учиться — не только для того, кто допустил ошибку, но и для остальных участников проекта. А когда старшие разработчики и менеджеры бесстрашно признают свои ошибки, перспектива становится менее пугающей для младших разработчиков.
- Предоставьте учебные ресурсы. Это ключ к развитию культуры обучения.

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

Источник: https://stackoverflow.blog/2024/06/19/the-real-10x-developer-makes-their-whole-team-better/
👍16👎1
День 1982. #ЧтоНовенького #CSharp13
ReadOnlySet<T> в .NET 9

Очередная, шестая, превью версия .NET 9 представит новый тип ReadOnlySet<T>. Это множество только для чтения, аналогичное ReadOnlyCollection<T>. Посмотрим, как это работает и зачем оно добавлено.

IReadOnlySet недостаточно
У нас уже есть интерфейс IReadOnlySet<T>. Например, его реализует FrozenSet. Почему его недостаточно? Рассмотрим List<T> и IReadOnlyList<T>, которые страдают от той же проблемы:
var list = new List<int> { 1, 2, 3 };
IReadOnlyList<int> readOnlyList = list;

// Вы можете привести это обратно к List<T> и изменить
var list2 = (List<int>)readOnlyList;
list2.Add(4);

Console.WriteLine($"List: {list.Count}");
Console.WriteLine($"ReadOnlyList: {readOnlyList.Count}");
Console.WriteLine($"List2: {list2.Count}");

Вывод (внезапно):
List: 4
ReadOnlyList: 4
List2: 4

Конечно, потребителям вашего API не следует этого делать, но вы не можете этого предотвратить. Вот почему у нас есть AsReadOnly:
List<int> list = [1, 2, 3];
var readOnlyList = list.AsReadOnly();

// Вы не можете привести это к List<T>
var list2 = (List<int>)readOnlyList; // Исключение

AsReadOnly возвращает ReadOnlyCollection<T>, поэтому приведение её к изменяемому списку невозможно.

Той же проблемой страдает и IReadOnlySet<T>. Именно для этого создан ReadOnlySet<T>:
var set = new HashSet<int> { 1, 2, 3 };
var readonlySet = new ReadOnlySet<int>(set);


Неправильный обходной путь
Если попытаться реализовать собственный способ создания множества только для чтения, пришлось бы создать что-то вроде ImmutableHashSet<T> или FrozenSet.
var set = new HashSet<int> { 1, 2, 3 };
var readOnly = set.ToFrozenSet(); // или ToImmutableHashSet()

Хотя технически это работает, у этого две основные проблемы:
1. Неизменяемые коллекции, такие как ImmutableHashSet<T> или FrozenSet, должны cкопировать всю коллекцию в свою память, чтобы гарантировать неизменяемость. Это может быть пустой тратой памяти и циклов процессора.
2. Это не совсем множество только для чтения. «Только для чтения» и «неизменяемый» — это два разных понятия, как мы разобрали недавно.

Подробнее о предложении новинки тут.

Источник: https://steven-giesel.com/blogPost/f368c7d3-488e-4bea-92b4-abf176353fa3/readonlysett-in-net-9
👍11
День 1983. #УрокиРазработки
Уроки 50 Лет Разработки ПО

Урок 14. Большая группа людей не способна организованно покинуть горящую комнату, не говоря уж о том, чтобы сформулировать требование
Большой группе людей трудно договариваться и принимать решения. Участники легко отвлекаются на посторонние разговоры. В большой группе всегда найдётся несколько человек, которым будет что сказать по теме, что приведёт к более длительному, но не всегда плодотворному обсуждению. Разногласия могут перерасти в продолжительные споры. Одни участники могут доминировать в обсуждении, а другие — полностью отключиться.

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

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

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

Фокус
Координатор должен сначала рассмотреть горизонтальный охват — часть требований пользователей, которые группа должна обсудить на конкретном семинаре. Противоположность - вертикальный охват — глубина исследования выбранных элементов. Семинары не предназначены для выявления всех деталей каждого требования. Достаточно узнать о требованиях ровно столько, чтобы команда могла оценить их размеры и определить порядок реализации. Уделите достаточно времени обсуждению формулировок, чтобы все участники поняли суть каждого требования. Детали бизнес-аналитик обсудит отдельно с разными пользователями, например непосредственно перед реализацией требования.

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

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

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

Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 2.
👍3
День 1984. #Оффтоп
Сравнение WebAssembly и JavaScript
WebAssembly (Wasm) и JavaScript — две ключевые технологии в современной веб-разработке, каждая имеет свои сильные стороны и области применения.

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

Портативность и совместимость
И Wasm, и JS хорошо переносимы и работают во всех основных браузерах и платформах. Wasm поддерживает несколько языков программирования: C, C++, C#, Rust и т.д., - компилируются в Wasm и запускаются в сети. Это открывает двери для большего числа разработчиков и позволяет повторно использовать код из других сред.
JS же пользуется универсальной поддержкой и может похвастаться огромной экосистемой фреймворков и библиотек, что делает его стандартным для большинства веб-проектов.

Простота использования
Низкая кривая обучения и динамическая типизация делают JS удобным для новичков. Обширная документация, активное сообщество, множество инструментов и фреймворков упрощают разработку.
Wasm мощный, но более сложный. Требует знания одного из поддерживаемых языков и понимания процесса компиляции. Его экосистема все ещё развивается и предлагает меньше ресурсов, чем JS.

Безопасность
И Wasm, и JS работают в изолированных средах, защищая хост-систему. Динамическая природа JS делают его уязвимым для таких атак, как межсайтовый скриптинг (XSS), если не обрабатывать пользовательский ввод должным образом. Бинарный формат Wasm делают его более устойчивым к определённым атакам на внедрение кода. Но всё же использование лучших практик каждого языка критично для обеспечения безопасности.

Принятие и использование
В 2024 Wasm растёт на 23% по сравнению с 2023. Такие отрасли, как игры, финансы и здравоохранение, используют Wasm для создания высокопроизводительных веб-приложений, требующих обработки в реальном времени.
JS остается основой веб-разработки: его регулярно используют более 60% разработчиков. Универсальность и обширная экосистема делают его незаменимым для широкого спектра применений.

Инновации и обновления
Системный интерфейс WebAssembly (WASI) упрощает запуск Wasm вне браузера, открывая новые варианты использования, такие как серверные приложения и устройства IoT. Модель компонентов Wasm также была улучшена, что сделало модульность и повторное использование модулей Wasm более эффективными.
JS продолжает развиваться благодаря новым предложениям ECMAScript. В 2024 стандартизированы улучшенное сопоставление по шаблону, улучшенные возможности асинхронного программирования и модульность.

Инструменты и фреймворки
Такие проекты, как Wasmtime и Wasmer, упростили запуск Wasm на серверах. Bytecode Alliance активно разрабатывает новые библиотеки и инструменты.
Фреймворки JS, такие как React, Vue и Angular, продолжают улучшаться, их обновления ориентированы на производительность, опыт разработчиков и интеграцию современных веб-стандартов.

Реальные истории успеха
AutoDesk использовала Wasm для повышения производительности своих инструментов. Финансовые учреждения используют Wasm для сложных расчётов в реальном времени.
Такие компании, как Airbnb и Netflix, используют JS для создания удобных интерактивных интерфейсов. Универсальность JS позволяет этим компаниям быстро внедрять новые функции.

Источник: https://www.sitepoint.com/webassembly-javascript-comparison/
👍10👎1
Какой вид кэша в ASP.NET имеет встроенную защиту от "паники в кэше"?
#Quiz #ASPNET
Anonymous Quiz
25%
IMemoryCache
33%
IDistributedCache
42%
Никакой
👍5
День 1985. #Карьера
Полезные и Игнорируемые Навыки
Есть полезный, но недооцененный навык: принимать определённую степень хлопот и абсурда, когда этого требует реальность.

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

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

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

3. Возможность поговорить 10 минут с кем угодно.
Технологии заменили многие личные разговоры. Сидеть с кем-то, кого вы никогда не встречали, смотреть ему в глаза и поддерживать разговор – то, что раньше было настолько обычным явлением, что не считалось навыком – теперь является конкурентным преимуществом.

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

5. Дипломатично говорить: «Нет».
«Нет» часто произносится двумя разрушительными способами. Во-первых, человеку неприятно говорить «нет», поэтому он тянет или говорит «да», откладывая неизбежное «нет». Так вы не держите слово, и другой человек будет разочарован больше, чем если бы вы сразу отказали. Другой вариант —непреднамеренное резкое «нет», из-за которого другой человек больше никогда не захочет привлекать ваше внимание к какой-либо идее или проблеме. Дипломатичное «нет» — это когда вы даёте однозначный ответ, но заботитесь о том, как собеседник может интерпретировать его.

6. Уважать удачу так же, как и риск.
На результаты могут влиять события, находящиеся вне вашего контроля. И удача, и риск происходят потому, что мир слишком сложен, чтобы позволить 100% ваших действий определять 100% ваших результатов. Но риск легко замечать, потому что он – приемлемая отмазка, когда что-то идёт не так. Удача – наоборот. Больно думать, что часть, а может и весь ваш успех не был вызван вашими действиями. Так удача преуменьшается и игнорируется в отличие от риска. Способность осознавать, что ваши победы могут не сигнализировать о том, что вы сделали что-то правильно, так же как ваши поражения могут не сигнализировать о том, что вы сделали что-то неправильно, жизненно важна для изучения чего-то ценного на основе обратной связи из реального мира.

Источник: https://collabfund.com/blog/useful-and-overlooked-skills/
👍16👎1
День 1986. #ЗаметкиНаПолях #Cancellation
Отмена. Часть 1: Обзор. Начало

Отмена — это тема, о которой возникает множество вопросов. И хотя документация Microsoft довольно хороша, в этой серии подробно рассмотрим эту тему.

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

Важный вывод: отвечающий код должен ответить на запрос отмены, чтобы отмена действительно что-то отменила.

Обычно возникает вопрос: как отменить неотменяемый код? Это сложный сценарий, рассмотрим его позже.

Токены отмены и «90% случаев»
В .NET токен отмены является «носителем» запроса на отмену. Запрашивающий код вызывает отмену на токене, а отвечающий код реагирует на это. Т.е. токен отмены — это то, как запрос отмены передается от запрашивающего кода к отвечающему коду.

Поэтому в 90% случаев добавьте параметр CancellationToken в метод, а затем передайте его любому API, который вы вызываете:
async Task DoAsync(
int data,
CancellationToken ct)
{
var myVal = await DoFirstAsync(data, ct);
await DoSecondAsync(myVal, ct);
}

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

Контракт отмены: сигнатура метода
По соглашению параметр CancellationToken обычно является последним, если не присутствует параметр IProgress<T>. Обычно предоставляется перегрузка или значение по умолчанию, чтобы вызывающему коду не приходилось предоставлять CancellationToken, если его нет; значение CancellationToken по умолчанию - CancellationToken.None, т.е. токен, который никогда не будет отменён:
async Task DoAsync(int data) => 
DoAsync(data, CancellationToken.None);

async Task DoAsync(int data, CancellationToken ct)
{

}

// либо
async Task DoAsync(
int data, CancellationToken ct = default)
{

}

Некоторые сигнатуры методов принимают CancellationToken и значение тайм-аута как отдельные параметры. В основном это делается в BCL, чтобы обеспечить более эффективные методы p/Invoke, которые принимают параметры тайм-аута. Если не используете API p/Invoke, используйте только CancellationToken, который может представлять собой любой вид отмены.

Принимая параметр CancellationToken, метод неявно заявляет, что он может отреагировать на отмену. Технически это означает «может ответить», а не «должен ответить». В некоторых случаях (например, при реализации интерфейса) аргумент CancellationToken может игнорироваться. Таким образом, наличие параметра CancellationToken не обязательно означает, что код должен поддерживать отмену.

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

Источник:
https://blog.stephencleary.com/2022/02/cancellation-1-overview.html
👍20
День 1987. #ЗаметкиНаПолях #Cancellation
Отмена. Часть 1: Обзор. Окончание

Начало

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

Стандартный код для «90% случаев» обрабатывает это неявно; если DoFirstAsync или DoSecondAsync выбрасывают OperationCanceledException, то это исключение также распространяется из DoAsync. Никаких изменений в коде в «90% случаев» не требуется:
async Task DoAsync(
int data,
CancellationToken ct)
{
var myVal = await DoFirstAsync(data, ct);
await DoSecondAsync(myVal, ct);
}

Существует множество примеров кода, которые просто молча делают ранний возврат, когда запрашивается отмена. Пожалуйста не делайте так; это нарушение контракта отмены! Когда отвечающий код просто возвращается раньше, вызывающий код не может знать, был ли его запрос на отмену удовлетворён или проигнорирован.

Исключение из «90% случаев»
Код в «90% случаев» просто принимает параметр CancellationToken и передаёт его дальше. Из этого правила есть одно заметное исключение: не следует передавать токен отмены в Task.Run.

Причина в том, что семантика сбивает с толку. Многие разработчики передают делегат и токен отмены в Task.Run и ожидают, что делегат будет отменён при отмене токена, но этого не происходит. Токен отмены, передаваемый в Task.Run, просто отменяет планирование делегата в пуле потоков; как только этот делегат начинает работать (что происходит практически сразу), этот токен отмены игнорируется.

Вот что пишут многие разработчики, ошибочно ожидая, что // Что-то делаем будет отменено после его запуска:
async Task DoSomethingAsync(CancellationToken ct)
{
await Task.Run(() =>
{
// Что-то делаем
}, ct);

}

Никогда не передавая CancellationToken в Task.Run (который в любом случае игнорируется, если только не возникает серьёзная конкуренция за пул потоков или токен уже не отменен), мы яснее даём понять, что сам делегат должен реагировать на токен:
async Task DoSomethingAsync(CancellationToken ct)
{
await Task.Run(() =>
{
// Что-то делаем
// IDE сообщает, что ct не используется,
// поэтому делегату нужно его использовать.
});

}


Источник: https://blog.stephencleary.com/2022/02/cancellation-1-overview.html
👍20
День 1988. #ЧтоНовенького
Оценка Кода с Помощью .NET Upgrade Assistant
Я уже писал про инструмент Upgrade Assistant ранее. Сегодня посмотрим, какие обновления он получил.

Независимо от того, выполняете ли вы обновление с .NET Framework до .NET 8 или просто между версиями .NET Core (с .NET 6 или 7 до .NET 8 или 9), .NET Upgrade Assistant поможет вам понять, какие изменения потребуются. Он доступен как расширение Visual Studio или как инструмент командной строки. Теперь в рамках обновления вы получите доступ к мощным функциям оценки кода.

Инструмент оценки кода сканирует решение на уровне исходного кода и выявляет потенциальные проблемы, которые могут возникнуть в процессе обновления. Вам будет предоставлена ​​комплексная информационная панель, которая предоставит обзор проблем во всём решении и оценку того, сколько усилий потребуется для устранения каждой проблемы.

Каждая проблема в отчете классифицирована по степени серьезности: обязательная проблема, блокирующее обновление, или дополнительная возможность новой версии .NET.

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

Анализ в Visual Studio
После установки расширения Visual Studio запустить инструмент обновления можно, просто щелкнув правой кнопкой мыши на решении и выбрав пункт Upgrade (Обновить).

Инструмент откроет окно, предлагающее либо создать новый отчёт, либо открыть существующий. Для нового отчёта вам будет предложено выбрать проекты для анализа, и предпочитаемую целевую платформу для обновления. Далее нужно выбрать компоненты из ранее выбранных проектов для анализа: только исходный код и настройки или также включить все бинарные зависимости. После этого выбора нажмите Analyze (Анализ), чтобы начать оценку приложения и получить отчет.

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

Вы можете просмотреть саммари проблем для всего решения или углубиться в представления конкретного проекта. Здесь вы можете просмотреть подробную информацию о каждом инциденте, получить помощь по их устранению, перейти к их местоположению в коде и так далее. Можно просматривать проблемы по типу проблемы или по компоненту, чтобы понять, что создаёт основную часть проблем в отчёте. Отчёты можно сохранять в форматах HTML, CSV и JSON, чтобы делиться с коллегами.

Источник: https://devblogs.microsoft.com/visualstudio/code-assessment-with-net-upgrade-assistant/
👍9
День 1989. #CSharp13
⚡️BREAKING! Типов-Расширений не Будет в C#13
О типах-расширениях я уже рассказывал, их с помпой представили Мэдс Торгенсен и Дастин Кэмбелл аж на Microsoft Build как главную новинку C# 13. Но позавчера в очередном обзоре новинок Кэтлин Доллард, главный программный менеджер .NET, сообщила, что «разработка и реализация потребуют больше времени. Ищите типы расширений в ранних предварительных версиях C#14 (NET 10)».

Подробности проблемы были описаны в саммари встречи о дизайне языка. Идея с генератором кода расширений, использовавшего Unsafe.As, «была отвергнута архитекторами среды выполнения .NET как принципиально небезопасная. Существуют потенциальные проблемы с псевдонимами, которые могут привести к путанице в частях JIT, особенно в редких сценариях оптимизации, и нельзя гарантировать, что они всегда будут безопасно обрабатываться с учётом предполагаемых вариантов использования. Нужно будет научить среду выполнения обрабатывать эти сценарии. Это долгосрочная функция, и нам потребуется время, чтобы опробовать её с реальными пользователями, прежде чем мы будем полностью уверены, что у нас правильный дизайн, поэтому мы хотели вернуться к чертёжной доске.

Мы видим два основных подхода:
1) Полностью посвятить себя поддержке во время выполнения. Если бы мы хотели пойти по пути поддержки во время выполнения, мы бы не полагались только на Unsafe.As; мы бы обновили среду выполнения, чтобы напрямую разрешить присвоение ref базового типа типу расширения. Это было бы безопаснее на уровне выполнения, поскольку даёт примитив, который можно проверить. Это также может дать лучшую отправную точку для дальнейшей поддержки членов интерфейса. Однако, к сожалению, мы не сможем достаточно обкатать функциональность до выхода .NET 9. Мы не боимся, что обещанные функции не будут выпущены, если они не готовы. Но это, скорее всего, будет означать, что расширения не будут выпущены в стабильной форме, по крайней мере, до выхода .NET 11.

2) Вернуться к рассмотрению расширений как сахара над статическими методами для типов расширений, что очень похоже на современные методы расширения. Здесь возникают повышенные трудности с представлением компилятора: публичная модель (сахар) и внутренняя реализация модели этих типов должны будут конвертироваться между собой. И дальнейшее расширение сахара возможно, но будет иметь некоторые сложности. Также потребуется больше переписывать тела членов, чтобы имитировать семантику методов экземпляра. Преимущества:
- Такая версия может быть выпущена в период с выходом .NET 9, по крайней мере, в предварительной версии.
- Существующие методы расширения фактически могут быть преобразованы в новую форму. Это будет не идеально; в частности, любой класс-держатель методов расширения, имеющий расширения для нескольких типов, скорее всего, не сможет работать.»


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

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

С другой стороны, по большому счёту из новинок #CSharp13 теперь и выделить то нечего. Так, мелкие улучшения в довольно нишевых сценариях.

Что думаете?

Источники:
-
https://devblogs.microsoft.com/dotnet/csharp-13-explore-preview-features/#update-on-extension-types
-
https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-06-12.md#extensions
👍13