Из этой статьи вы узнаете о библиотеке Microsoft.Extensions.Primitives. Примитивы в этой статье не следует путать с примитивными типами .NET от BCL или языка C#. Вместо этого типы в библиотеке примитива служат стандартными блоками для некоторых периферийных пакетов NuGet .NET, таких как:
Распространение уведомлений при возникновении изменений является фундаментальным понятием в программировании. Наблюдаемое состояние объекта чаще всего не может измениться. Когда происходит изменение, реализации интерфейса Microsoft.Extensions.Primitives.IChangeToken можно использовать для уведомления заинтересованных в нем сторон. Доступны следующие реализации:
IChangeToken.ActiveChangeCallbacks: указывает, будет ли токен инициировать обратные вызовы с упреждением. Если имеет значение false, получатель токена должен опрашивать HasChanged для обнаружения изменений.
Функциональные возможности на основе экземпляра
Рассмотрим следующий пример использования CancellationChangeToken.
C#
CancellationTokenSource cancellationTokenSource = new();
CancellationChangeToken cancellationChangeToken = new(cancellationTokenSource.Token);
Console.WriteLine($"HasChanged: {cancellationChangeToken.HasChanged}");
staticvoidcallback(object? _) =>
Console.WriteLine("The callback was invoked.");
using (IDisposable subscription =
cancellationChangeToken.RegisterChangeCallback(callback, null))
{
cancellationTokenSource.Cancel();
}
Console.WriteLine($"HasChanged: {cancellationChangeToken.HasChanged}\n");
// Outputs:// HasChanged: False// The callback was invoked.// HasChanged: True
В предыдущем примере создается экземпляр CancellationTokenSource и его Token передается в конструктор CancellationChangeToken. Начальное состояние HasChanged записывается в консоль. Создается объект Action<object?> callback, который выполняет операции записи при запуске обратного вызова в консоль. Вызывается метод RegisterChangeCallback(Action<Object>, Object) с учетом callback. В операторе using отменяется cancellationTokenSource. Это активирует обратный вызов, а состояние HasChanged снова записывается в консоль.
Если необходимо выполнить действия из нескольких источников изменений, используйте CompositeChangeToken. Эта реализация объединяет один или несколько токенов изменения и запускает каждый зарегистрированный обратный вызов всего один раз, независимо от того, сколько раз было инициировано изменение. Рассмотрим следующий пример:
C#
CancellationTokenSource firstCancellationTokenSource = new();
CancellationChangeToken firstCancellationChangeToken = new(firstCancellationTokenSource.Token);
CancellationTokenSource secondCancellationTokenSource = new();
CancellationChangeToken secondCancellationChangeToken = new(secondCancellationTokenSource.Token);
CancellationTokenSource thirdCancellationTokenSource = new();
CancellationChangeToken thirdCancellationChangeToken = new(thirdCancellationTokenSource.Token);
var compositeChangeToken =
new CompositeChangeToken(
new IChangeToken[]
{
firstCancellationChangeToken,
secondCancellationChangeToken,
thirdCancellationChangeToken
});
staticvoidcallback(object? state) =>
Console.WriteLine($"The {state} callback was invoked.");
// 1st, 2nd, 3rd, and 4th.
compositeChangeToken.RegisterChangeCallback(callback, "1st");
compositeChangeToken.RegisterChangeCallback(callback, "2nd");
compositeChangeToken.RegisterChangeCallback(callback, "3rd");
compositeChangeToken.RegisterChangeCallback(callback, "4th");
// It doesn't matter which cancellation source triggers the change.// If more than one trigger the change, each callback is only fired once.
Random random = new();
int index = random.Next(3);
CancellationTokenSource[] sources = new[]
{
firstCancellationTokenSource,
secondCancellationTokenSource,
thirdCancellationTokenSource
};
sources[index].Cancel();
Console.WriteLine();
// Outputs:// The 4th callback was invoked.// The 3rd callback was invoked.// The 2nd callback was invoked.// The 1st callback was invoked.
В предыдущем коде C# создаются три экземпляра объектов CancellationTokenSource и связываются с соответствующими экземплярами CancellationChangeToken. Создание экземпляра составного токена выполняется путем передачи массива токенов в конструктор CompositeChangeToken. Создается объект Action<object?> callback, но на этот раз объект state используется и записывается в консоль как форматированное сообщение. Обратный вызов регистрируется четыре раза, каждый из которых имеет несколько отличающийся аргумент объекта состояния. Код использует генератор псевдослучайных чисел для выбора одного из источников токенов изменения (неважно какого) и вызова его метода Cancel(). Это активирует изменение, что вызывает каждый зарегистрированный обратный вызов всего один раз.
Альтернативный подход static
В качестве альтернативы вызову RegisterChangeCallback можно использовать статический класс Microsoft.Extensions.Primitives.ChangeToken. Рассмотрим следующий шаблон потребления.
C#
CancellationTokenSource cancellationTokenSource = new();
CancellationChangeToken cancellationChangeToken = new(cancellationTokenSource.Token);
IChangeToken producer()
{
// The producer factory should always return a new change token.// If the token's already fired, get a new token.if (cancellationTokenSource.IsCancellationRequested)
{
cancellationTokenSource = new();
cancellationChangeToken = new(cancellationTokenSource.Token);
}
return cancellationChangeToken;
}
voidconsumer() => Console.WriteLine("The callback was invoked.");
using (ChangeToken.OnChange(producer, consumer))
{
cancellationTokenSource.Cancel();
}
// Outputs:// The callback was invoked.
Как и в предыдущих примерах, потребуется реализация IChangeToken, созданная changeTokenProducer. Производитель определяется как Func<IChangeToken>. Ожидается, что он будет возвращать новый токен при каждом вызове. consumer является либо Action, если не используется state, либо Action<TState>, где универсальный тип TState проходит через уведомление об изменениях.
Создатели маркеров строк, сегменты и значения
Взаимодействие со строками распространено в разработке приложений. Выполняется анализ или разбивка различных представлений строк либо по ним выполняется итерация. Библиотека примитивов предлагает несколько типов выбора, которые помогают оптимизировать взаимодействие со строками и сделать его более эффективным. Рассмотрим следующие типы.
StringSegment: оптимизированное представление substring.
StringTokenizer: создает маркер string в экземплярах StringSegment.
StringValues: представляет null, ноль, одну или множество строк эффективным способом.
Тип StringSegment.
В этом разделе описано оптимизированное представление substring, известное как тип StringSegmentstruct. Рассмотрим следующий пример кода C#, демонстрирующий некоторые свойства StringSegment и метод AsSpan.
C#
var segment =
new StringSegment(
"This a string, within a single segment representation.",
14, 25);
Console.WriteLine($"Buffer: \"{segment.Buffer}\"");
Console.WriteLine($"Offset: {segment.Offset}");
Console.WriteLine($"Length: {segment.Length}");
Console.WriteLine($"Value: \"{segment.Value}\"");
Console.Write("Span: \"");
foreach (char @charin segment.AsSpan())
{
Console.Write(@char);
}
Console.Write("\"\n");
// Outputs:// Buffer: "This a string, within a single segment representation."// Offset: 14// Length: 25// Value: " within a single segment "// " within a single segment "
Структура StringSegment предоставляет множество методов для взаимодействия с сегментом.
Тип StringTokenizer.
Объект StringTokenizer представляет собой тип структуры, который разбивает string на экземпляры StringSegment. Разметка больших строк обычно включает разделение строки и итерацию по ней. С учетом сказанного, возможно, стоит обратить внимание на String.Split. Эти API похожи, но в целом StringTokenizer обеспечивает лучшую производительность. Рассмотрим следующий пример.
C#
var tokenizer =
new StringTokenizer(
s_nineHundredAutoGeneratedParagraphsOfLoremIpsum,
new[] { ' ' });
foreach (StringSegment segment in tokenizer)
{
// Interact with segment
}
В предыдущем коде экземпляр типа StringTokenizer создается с учетом 900 автоматически сгенерированных абзацев текста Lorem Ipsum и массива с одним значением символа пробела ' '. Каждое значение в создателе маркеров представлено как StringSegment. Код выполняет итерацию сегментов, позволяя объекту-получателю взаимодействовать с каждым segment.
Сравнение производительности StringTokenizer и string.Split
С учетом различных способов сегментирования строк уместно сравнить два метода с помощью тестирования. Рассмотрим следующие два метода тестирования при использовании пакета NuGet BenchmarkDotNet.
Оба метода выглядят одинаково в контактной зоне интерфейса API, и они способны разбивать большую строку на фрагменты. Результаты тестирования производительности ниже показывают, что подход StringTokenizer почти в три раза быстрее, но результаты могут отличаться. Как и во всех вопросах производительности, вам следует оценить свой конкретный вариант использования.
Способ
Среднее
Ошибка
StdDev
Коэффициент
Tokenizer
3.315 мс
0.0659 мс
0.0705 мс
0,32
Разделение
10.257 мс
0.2018 мс
0.2552 мс
1.00
Условные обозначения
Среднее значение: среднее арифметическое всех измерений
Ошибка: половина от 99,9 % интервала достоверности
Стандартное отклонение: стандартное отклонение всех измерений
Медиана: значение, отделяющее большую часть всех измерений (50-й процентиль)
Коэффициент: среднее значение распределения коэффициентов (текущее/базовое)
Дополнительные сведения о тестировании с помощью .NET см. в разделе BenchmarkDotNet.
Тип StringValues.
Объект StringValues является типом struct, который эффективным способом представляет null, ноль, одну или множество строк. Тип StringValues можно создать с помощью любого из следующих синтаксисов: string? или string?[]?. Используя текст из предыдущего примера, рассмотрим следующий код C#.
C#
StringValues values =
new(s_nineHundredAutoGeneratedParagraphsOfLoremIpsum.Split(
new[] { '\n' }));
Console.WriteLine($"Count = {values.Count:#,#}");
foreach (string? valuein values)
{
// Interact with the value
}
// Outputs:// Count = 1,799
Приведенный выше код создает экземпляры объекта StringValues с учетом массива значений строк. Записывается StringValues.Count в консоль.
Тип StringValues является реализацией следующих типов коллекций:
IList<string>
ICollection<string>
IEnumerable<string>
IEnumerable
IReadOnlyList<string>
IReadOnlyCollection<string>
Таким образом, можно выполнять итерацию по нему и с каждым value можно взаимодействовать по мере необходимости.
Источник этого содержимого можно найти на GitHub, где также можно создавать и просматривать проблемы и запросы на вытягивание. Дополнительные сведения см. в нашем руководстве для участников.
Отзыв о .NET
.NET — это проект с открытым исходным кодом. Выберите ссылку, чтобы оставить отзыв:
Присоединитесь к серии встреч для создания масштабируемых решений искусственного интеллекта на основе реальных вариантов использования с другими разработчиками и экспертами.