.NET Разработчик
6.54K subscribers
442 photos
3 videos
14 files
2.12K links
Дневник сертифицированного .NET разработчика. Заметки, советы, новости из мира .NET и C#.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День 2028. #ЧтоНовенького #CSharp13
Альтернативный Доступ к Коллекциям

До C#13 ref-структуры не могли быть аргументами параметра-типа в обобщённых типах. Теперь в объявление обобщённого типа после ключевого слова where добавлено «анти-ограничение» allows ref struct, которое сообщает, что аргументом для параметра типа может быть ref-структура. Анти-ограничение оно, потому что по факту оно не ограничивает возможный набор параметров типа, а расширяет его. Это анти-ограничение налагает все доступные для ref-структур правила (вроде запрета размещения в куче) на все экземпляры параметра-типа. Смысл этого изменения также в том, чтобы можно было использовать типы Span в обобщённых-алгоритмах.

Например, таблицы поиска (lookup-таблицы) типа Dictionary<TKey,TValue> и HashSet<T> часто используются в качестве своего рода кэша. Однако, так как ключи таблиц обычно строкового типа, раньше приходилось выделять строку, чтобы обратиться в lookup-таблицу по ключу. Теперь упомянутое выше «анти-ограничение» добавляет новые возможности для этих типов коллекций.

Следующий код считает количество различных слов в тексте, используя так называемые альтернативные ключи словаря:
static Dictionary<string, int> 
CountWords(ReadOnlySpan<char> input)
{
Dictionary<string, int> counts =
new(StringComparer.OrdinalIgnoreCase);

Dictionary<string, int>.AlternateLookup<ReadOnlySpan<char>>
lookup = counts.GetAlternateLookup<string, int, ReadOnlySpan<char>>();

foreach (Range r in Regex.EnumerateSplits(input, @"\b\W+\b"))
{
ReadOnlySpan<char> word = input[r];
lookup[word] = lookup
.TryGetValue(word, out int count) ? count + 1 : 1;
}

return counts;
}

Сначала мы создаём lookup-словарь counts и указываем, что нам не важен регистр ключей.
Затем, чтобы искать в словаре по spanам, а не по строкам, мы создаём структуру lookup для альтернативного доступа к элементам. Здесь она вот такого длинного типа Dictionary<string, int>.AlternateLookup<ReadOnlySpan<char>>. В примере указан тип полностью для демонстрации. В реальном коде это можно заменить на var. Заметьте, что тут используется обобщённый тип AlternateLookup с параметром типа виде ref-структуры ReadOnlySpan<char>.
Далее мы разбиваем текст по «не-словам», используя Regex.EnumerateSplits. В результате получаем перечислитель диапазонов результатов. С его помощью мы делаем срезы исходного текста, которые представляют собой отдельные слова. Это переменная word типа ReadOnlySpan<char>. И вот здесь, чтобы не создавать строк для доступа в словарь counts по строковому ключу, нам и потребуется структура lookup, которая обеспечит доступ к словарю по альтернативному ключу типа ReadOnlySpan<char>. В данном случае мы увеличиваем счётчик для текущего слова.

Теперь мы можем взять текст и посчитать в нём слова:
var text = "<какой-то длинный текст>";
foreach (var word in CountWords(text))
Console.WriteLine(word);


Источник: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-13
👍20
День 2032. #ЧтоНовенького #CSharp13
Флаги функций с поддержкой тримминга
Два новых атрибута позволяют определять флаги функций, которые можно использовать для включения/отключения областей функциональности, а также для автоматического включения/отключения функций при тримминге или AOT-компиляции.

FeatureSwitchDefinitionAttribute
Атрибут FeatureSwitchDefinition может быть использован, чтобы флаг функции после сборки определялся как константа.
public class Feature
{
[FeatureSwitchDefinition("Feature.IsSupported")]
internal static bool IsSupported =>
AppContext.TryGetSwitch("Feature.IsSupported", out bool enabled)
? enabled
: true;

internal static void Implementation() => …;
}

Здесь мы извлекаем значение IsSupported из конфигурации сборки. В данном случае оно определено в файле проекта (.csproj):
<ItemGroup>
<RuntimeHostConfigurationOption Include="Feature.IsSupported"
Value="false" Trim="true" />
</ItemGroup>

Мы добавляем RuntimeHostConfigurationOption с именем нужной функции (соответствующим параметру атрибута) и булевым значением, включена она или нет.

При сборке с включённым триммингом недосягаемый код, находящийся под флагом, удаляется. Когда приложение собирается с такой настройкой в файле проекта, Feature.IsSupported расценивается как константа false и Feature.Implementation удаляется из сборки.

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

FeatureGuardAttribute
Атрибут FeatureGuard можно использовать для свойства флага функции в качестве защитной конструкции для кода, аннотированного атрибутами RequiresUnreferencedCode, RequiresAssemblyFiles или RequiresDynamicCode:
public class Feature
{
[FeatureGuard(typeof(RequiresDynamicCodeAttribute))]
internal static bool IsSupported =>
RuntimeFeature.IsDynamicCodeSupported;

[RequiresDynamicCode("Feature requires dynamic code support.")]
internal static void DynamicImplementation()
=> …; // Использует dynamic
}

Здесь RuntimeFeature определяет API, которые доступны в среде выполнения. В данном случае свойство IsDynamicCodeSupported показывает, поддерживается ли в среде выполнения динамический код.
Поскольку свойство IsSupported возвращает false, когда динамический код не поддерживается, его можно использовать в качестве защиты для методов, которым требуется динамический код во время выполнения.
if (Feature.IsSupported)
Feature.DynamicImplementation();

Атрибут FeatureGuard сообщает об этом анализатору тримминга и инструментам тримминга. Поэтому, если мы в файле проекта укажем
<PublishAot>true</PublishAot>

то Feature.DynamicImplementation будет удалён при AOT-публикации.

Источник: https://github.com/dotnet/core/blob/main/release-notes/9.0/preview/preview4/runtime.md
6👍7