День 1404. #ЧтоНовенького #DotNet8
Новые Анализаторы в .NET 8
.NET 7 вышел, пора посмотреть, что нас ждёт в новой версии))) Сегодня рассмотрим новые анализаторы кода. Анализаторы в версии превью можно использовать, если добавить в файл .csproj следующее:
Строки
1. Рекомендация заменить string.ToLower() на регистронезависимый Equals
По сути, правило предложит заменить
2. Рекомендация использовать StartsWith вместо IndexOf == 0
Проблема с IndexOf в том, что в худшем случае он пройдёт по всей строке. StartsWith, напротив, сразу прервётся при первом несоответствии, поэтому он быстрее.
Коллекции
3. Рекомендация использовать Length/Count/IsEmpty вместо Any()
Если в коллекции есть свойство вроде Length (T[]), Count (List<T>) или IsEmpty (потокобезопасные коллекции), то лучше использовать его, а не Any().
4. Предотвращение неправильного использования Split
Допустим, вы хотите разделить строку по пробелам и табам:
6. Атрибут NonCopyable
Обычно, если вы передаёте типы-значения в функцию, они копируются. В чём проблема? Предположим, вы пишете какой-то высокопроизводительный API (например, ValueStringBuilder), используя (ref) struct, чтобы избежать аллокаций на куче. Потребители этих типов должны быть осторожны, чтобы объект не копировался повсюду. И вот тут-то поможет новый атрибут.
Новые Анализаторы в .NET 8
.NET 7 вышел, пора посмотреть, что нас ждёт в новой версии))) Сегодня рассмотрим новые анализаторы кода. Анализаторы в версии превью можно использовать, если добавить в файл .csproj следующее:
<PropertyGroup Label="Analyzer settings">Уже сейчас есть более 100 правил, которые предупредят вас в определённых ситуациях. Полный список можно найти на странице GitHub. Сегодня рассмотрим, что нового собираются добавить.
<AnalysisLevel>preview</AnalysisLevel>
</PropertyGroup>
Строки
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().
// Это будет помечено новым анализаторомЭто может быть быстрее, т.к. Any выполняет проверку типов, а Length или Count — это простые свойства, которые, помимо прочего, могут быть встроены. Хотя, субъективно, в коде Any может выглядеть более читабельно.
var isEmpty = names.Any();
// Это не будет помечено как "предпочтительное"
var isEmpty = names.Count != 0;
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]Здесь будет ошибка, т.к. ValueStringBuilder должен передаваться по ссылке. Правильное использование:
public ref struct ValueStringBuilder
{ … }
// Использование
public void MyMethod(ValueStringBuilder vsb)
{ … }
var vsb = new ValueStringBuilder();
MyMethod(vsb);
public void MyMethod(ref ValueStringBuilder vsb)Источник: https://steven-giesel.com/blogPost/1a03a998-e428-4a42-9c30-1cfbf6ac5f4c
{ … }
var vsb = new ValueStringBuilder();
MyMethod(ref vsb);
👍20
День 1410. #ЧтоНовенького #DotNet8
Замороженные Коллекции
Продолжаем рассматривать, чего новенького могут предложить нам в 8й версии .NET. Сегодня рассмотрим, что такое замороженные коллекции и как они работают.
Вот код, который работает в альфа-версии .NET8:
ReadOnlyList — это просто «представление» объекта, из которого он был создан. Поэтому, если вы обновите список, из которого он был первоначально создан, ReadOnlyList также отразит это изменение. Пользователь ReadOnlyList просто не имеет возможности изменить внутреннее состояние самого списка.
Замораживаемую коллекцию можно изменять до тех пор, пока она не будет заморожена. После этого она больше не отражает изменений. Поэтому Count = 3.
Неизменяемый список также содержит только 3 элемента, но при этом неизменяемые коллекции имеют эффективные способы изменения списка путем создания нового:
Для замороженных коллекций такого поведения не предусмотрено.
На данный момент в .NET8 есть два типа замороженных коллекций: FrozenSet и FrozenDictionary. Возникает вопрос: зачем они?
Замороженные объекты вследствие своих ограничений могут дать преимущества в производительности. Часто коллекции инициализируются в какой-то момент, но никогда не меняют своего состояния. Например, посмотрим для списка из чисел от 1 до 1000 тест скорости метода Contains:
Особенности замороженных коллекций:
1. Они действительно доступны только для чтения и неизменны. Они создаются таким образом, чтобы данные были плотными и оптимально организованными для быстрого чтения.
2. При создании находится оптимальный размер таблицы сегментов, чтобы уменьшить или устранить коллизии хэшей, что сокращает среднее время поиска.
3. Особое внимание уделяется коллекциям со строковыми ключами. Различные компараторы выбираются динамически для повышения скорости хеширования во время поиска. Код просматривает все ключи и пытается найти кратчайшую подстроку, которая создаёт уникальный набор ключей. Обычно это значительно сокращает количество символов, используемых при вычислении хэш-кодов во время поиска.
4. Эти коллекции могут содержать не более 64КБ состояния, что покрывает 99% случаев.
Очевидно, что создание замороженной коллекции обходится дороже, чем создание простого списка. Это отражает типичный вариант их использования. Создать один раз в начале, а затем часто обращаться к ним (например, через поиск элемента).
Вот репозиторий с примерами кода и тестами.
Источник: https://steven-giesel.com/blogPost/34e0fd95-0b3f-40f2-ba2a-36d1d4eb5601
Замороженные Коллекции
Продолжаем рассматривать, чего новенького могут предложить нам в 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