.NET Разработчик
6.56K 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
День 1404. #ЧтоНовенького #DotNet8
Новые Анализаторы в .NET 8
.NET 7 вышел, пора посмотреть, что нас ждёт в новой версии))) Сегодня рассмотрим новые анализаторы кода. Анализаторы в версии превью можно использовать, если добавить в файл .csproj следующее:
<PropertyGroup Label="Analyzer settings">
<AnalysisLevel>preview</AnalysisLevel>
</PropertyGroup>

Уже сейчас есть более 100 правил, которые предупредят вас в определённых ситуациях. Полный список можно найти на странице GitHub. Сегодня рассмотрим, что нового собираются добавить.

Строки
1. Рекомендация заменить string.ToLower() на регистронезависимый Equals
По сути, правило предложит заменить
str.ToLowerCase() == "string";
на
string.Equals(str, "string", StringComparison.OrdinalIgnoreCase);
Это быстрее и не аллоцирует строки.

2. Рекомендация использовать StartsWith вместо IndexOf == 0
Проблема с IndexOf в том, что в худшем случае он пройдёт по всей строке. StartsWith, напротив, сразу прервётся при первом несоответствии, поэтому он быстрее.

Коллекции
3. Рекомендация использовать Length/Count/IsEmpty вместо Any()
Если в коллекции есть свойство вроде Length (T[]), Count (List<T>) или IsEmpty (потокобезопасные коллекции), то лучше использовать его, а не Any().
// Это будет помечено новым анализатором
var isEmpty = names.Any();
// Это не будет помечено как "предпочтительное"
var isEmpty = names.Count != 0;
Это может быть быстрее, т.к. Any выполняет проверку типов, а Length или Count — это простые свойства, которые, помимо прочего, могут быть встроены. Хотя, субъективно, в коде Any может выглядеть более читабельно.

4. Предотвращение неправильного использования Split
Допустим, вы хотите разделить строку по пробелам и табам:
var split = s.Split(' ', '\t');
Это прекрасно работает. Но если вы захотите удалить пустые элементы:
var split = s.Split(' ', '\t',
StringSplitOptions.RemoveEmptyEntries);
Но это не будет работать правильно, потому что второй char преобразуется в int и будет использован в качестве аргумента count (максимального количества элементов в результирующем массиве) для перегруженной версии Split. Правильное использование будет таким:
var split = s.Split(new[] { ' ', '\t' },
StringSplitOptions.RemoveEmptyEntries);

Структуры
6. Атрибут NonCopyable
Обычно, если вы передаёте типы-значения в функцию, они копируются. В чём проблема? Предположим, вы пишете какой-то высокопроизводительный API (например, ValueStringBuilder), используя (ref) struct, чтобы избежать аллокаций на куче. Потребители этих типов должны быть осторожны, чтобы объект не копировался повсюду. И вот тут-то поможет новый атрибут.
[NonCopyable]
public ref struct ValueStringBuilder
{ … }

// Использование
public void MyMethod(ValueStringBuilder vsb)
{ … }

var vsb = new ValueStringBuilder();
MyMethod(vsb);

Здесь будет ошибка, т.к. ValueStringBuilder должен передаваться по ссылке. Правильное использование:
public void MyMethod(ref ValueStringBuilder vsb)
{ … }

var vsb = new ValueStringBuilder();
MyMethod(ref vsb);

Источник: https://steven-giesel.com/blogPost/1a03a998-e428-4a42-9c30-1cfbf6ac5f4c
👍20
День 1410. #ЧтоНовенького #DotNet8
Замороженные Коллекции
Продолжаем рассматривать, чего новенького могут предложить нам в 8й версии .NET. Сегодня рассмотрим, что такое замороженные коллекции и как они работают.

Вот код, который работает в альфа-версии .NET8:
List<int> list = new() { 1, 2, 3 };
ReadOnlyCollection<int> readonlyList =
list.AsReadOnly();
ImmutableList<int> immutableList =
list.ToImmutableList();
FrozenSet<int> frozenSet =
list.ToFrozenSet();

list.Add(4);

list.Count; // 4
readonlyList.Count; // 4
immutableList.Count; // 3
frozenSet.Count; // 3

ReadOnlyList — это просто «представление» объекта, из которого он был создан. Поэтому, если вы обновите список, из которого он был первоначально создан, ReadOnlyList также отразит это изменение. Пользователь ReadOnlyList просто не имеет возможности изменить внутреннее состояние самого списка.

Замораживаемую коллекцию можно изменять до тех пор, пока она не будет заморожена. После этого она больше не отражает изменений. Поэтому Count = 3.
Неизменяемый список также содержит только 3 элемента, но при этом неизменяемые коллекции имеют эффективные способы изменения списка путем создания нового:
// это создаст новый неизменяемый список
var newList = immutableList.Add(2);

Для замороженных коллекций такого поведения не предусмотрено.

На данный момент в .NET8 есть два типа замороженных коллекций: FrozenSet и FrozenDictionary. Возникает вопрос: зачем они?

Замороженные объекты вследствие своих ограничений могут дать преимущества в производительности. Часто коллекции инициализируются в какой-то момент, но никогда не меняют своего состояния. Например, посмотрим для списка из чисел от 1 до 1000 тест скорости метода Contains:
public void Lookup()
{
for (var i = 0; i < 1000; i++)
_ = list.Contains(i);
}

List: 57.561 us
FrozenSet: 1.963 us
HashSet: 2.997 us
ImmutableHashSet: 15.422 us

Особенности замороженных коллекций:
1. Они действительно доступны только для чтения и неизменны. Они создаются таким образом, чтобы данные были плотными и оптимально организованными для быстрого чтения.
2. При создании находится оптимальный размер таблицы сегментов, чтобы уменьшить или устранить коллизии хэшей, что сокращает среднее время поиска.
3. Особое внимание уделяется коллекциям со строковыми ключами. Различные компараторы выбираются динамически для повышения скорости хеширования во время поиска. Код просматривает все ключи и пытается найти кратчайшую подстроку, которая создаёт уникальный набор ключей. Обычно это значительно сокращает количество символов, используемых при вычислении хэш-кодов во время поиска.
4. Эти коллекции могут содержать не более 64КБ состояния, что покрывает 99% случаев.

Очевидно, что создание замороженной коллекции обходится дороже, чем создание простого списка. Это отражает типичный вариант их использования. Создать один раз в начале, а затем часто обращаться к ним (например, через поиск элемента).

Вот репозиторий с примерами кода и тестами.

Источник: https://steven-giesel.com/blogPost/34e0fd95-0b3f-40f2-ba2a-36d1d4eb5601
👍17