Primitiva: Knihovna rozšíření pro .NET
V tomto článku se dozvíte o knihovně Microsoft.Extensions.Primitives . Primitiva v tomto článku se nezaměňují s primitivními typy rozhraní .NET z seznamu BCL nebo s primitivními typy jazyka C#. Místo toho typy v rámci primitivní knihovny slouží jako stavební bloky některých balíčků NuGet periferních rozhraní .NET, například:
Microsoft.Extensions.Configuration
Microsoft.Extensions.Configuration.FileExtensions
Microsoft.Extensions.FileProviders.Composite
Microsoft.Extensions.FileProviders.Physical
Microsoft.Extensions.Logging.EventSource
Microsoft.Extensions.Options
System.Text.Json
Změna oznámení
Šíření oznámení, když dojde ke změně, je základním konceptem programování. Pozorovaný stav objektu se častěji než nemůže změnit. Když dojde ke změně, lze implementace Microsoft.Extensions.Primitives.IChangeToken rozhraní použít k informování zúčastněných stran o této změně. Dostupné implementace jsou následující:
Jako vývojář můžete také implementovat vlastní typ. Rozhraní IChangeToken definuje několik vlastností:
- IChangeToken.HasChanged: Získá hodnotu, která označuje, zda došlo ke změně.
- IChangeToken.ActiveChangeCallbacks: Označuje, jestli token proaktivně vyvolá zpětné volání. Pokud
false
se příjemce tokenu musí dotazovatHasChanged
, aby zjistil změny.
Funkce založené na instancích
Podívejte se na následující příklad použití CancellationChangeToken
:
CancellationTokenSource cancellationTokenSource = new();
CancellationChangeToken cancellationChangeToken = new(cancellationTokenSource.Token);
Console.WriteLine($"HasChanged: {cancellationChangeToken.HasChanged}");
static void callback(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
V předchozím příkladu CancellationTokenSource se vytvoří instance a předá Token se konstruktoru CancellationChangeToken . Počáteční stav HasChanged
je zapsán do konzoly. Vytvoří se Action<object?> callback
zápis při vyvolání zpětného volání do konzoly. Metoda tokenu RegisterChangeCallback(Action<Object>, Object) je volána vzhledem k callback
. V rámci using
příkazu se cancellationTokenSource
zruší. Tím se aktivuje zpětné volání a stav HasChanged
se znovu zapíše do konzoly.
Pokud potřebujete provést akci z více zdrojů změn, použijte tlačítko CompositeChangeToken. Tato implementace agreguje jeden nebo více tokenů změn a aktivuje každou zaregistrovanou zpětné volání přesně jednou bez ohledu na počet aktivací změny. Představte si následující příklad:
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
});
static void callback(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.
V předchozím kódu jazyka C# se vytvoří a spárují tři CancellationTokenSource instance objektů s odpovídajícími CancellationChangeToken instancemi. Složený token se vytvoří instance předáním pole tokenů konstruktoru CompositeChangeToken . Vytvoří se Action<object?> callback
, ale tentokrát state
se objekt použije a zapíše do konzoly jako formátovaná zpráva. Zpětné volání je registrováno čtyřikrát, každý s mírně odlišným argumentem stavového objektu. Kód používá pseudonáhodný generátor čísel k výběru jednoho ze zdrojů tokenů změn (nezáleží na tom, který z nich) a zavolá jeho Cancel() metodu. Tím se tato změna aktivuje a vyvolání každého registrovaného zpětného volání přesně jednou.
Alternativní static
přístup
Jako alternativu k volání RegisterChangeCallback
můžete použít Microsoft.Extensions.Primitives.ChangeToken statickou třídu. Vezměte v úvahu následující model spotřeby:
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;
}
void consumer() => Console.WriteLine("The callback was invoked.");
using (ChangeToken.OnChange(producer, consumer))
{
cancellationTokenSource.Cancel();
}
// Outputs:
// The callback was invoked.
Podobně jako v předchozích příkladech budete potřebovat implementaci IChangeToken
, která je vytvořena pomocí changeTokenProducer
. Producent je definován jako Func<IChangeToken>
a očekává se, že se vrátí nový token při každém vyvolání. Jedná consumer
se o buď Action
při použití state
, nebo tam Action<TState>
, kde obecný typ TState
prochází oznámením o změně.
Tokenizátory řetězců, segmenty a hodnoty
Interakce s řetězci je běžná při vývoji aplikací. Různé reprezentace řetězců se analyzují, rozdělují nebo itestrují. Knihovna primitiv nabízí několik typů voleb, které pomáhají lépe optimalizovat a efektivněji pracovat s řetězci. Zvažte následující typy:
- StringSegment: Optimalizovaná reprezentace podřetědce.
- StringTokenizer: Tokenizuje instanci
string
StringSegment
. - StringValues: Představuje
null
efektivním způsobem , nula, jeden nebo mnoho řetězců.
Typ StringSegment
V této části se dozvíte o optimalizované reprezentaci podřetěděce, která se označuje jako StringSegment struct
typ. Představte si následující příklad kódu jazyka C#, který ukazuje některé vlastnosti StringSegment
a metodu AsSpan
:
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 @char in 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 "
Předchozí kód vytvoří StringSegment
instanci dané string
hodnoty, znaku offset
a znaku length
. Jedná se StringSegment.Buffer o původní řetězcový argument a StringSegment.Value je podřetězce založený na hodnotách StringSegment.Offset a StringSegment.Length hodnotách.
Struktura StringSegment
poskytuje mnoho metod pro interakci se segmentem.
Typ StringTokenizer
Objekt StringTokenizer je typ struktury, který tokenizuje do string
StringSegment
instancí. Tokenizace velkých řetězců obvykle zahrnuje rozdělení řetězce od sebe a iterování nad ním. S tímhle povídám, String.Split pravděpodobně přijde na mysl. Tato rozhraní API jsou podobná, ale obecně StringTokenizer poskytují lepší výkon. Nejprve zvažte následující příklad:
var tokenizer =
new StringTokenizer(
s_nineHundredAutoGeneratedParagraphsOfLoremIpsum,
new[] { ' ' });
foreach (StringSegment segment in tokenizer)
{
// Interact with segment
}
V předchozím kódu se vytvoří instance StringTokenizer
typu s 900 automaticky vygenerovanými Lorem Ipsum odstavci textu a pole s jednou hodnotou prázdného znaku ' '
. Každá hodnota v tokenizátoru je reprezentována jako StringSegment
. Kód iteruje segmenty, což příjemci umožňuje interakci s jednotlivými segment
segmenty .
Porovnání srovnávacích testů StringTokenizer
string.Split
S různými způsoby řezů a didikací řetězců je vhodné porovnat dvě metody s srovnávacím testem. Při použití balíčku NuGet BenchmarkDotNet zvažte následující dvě metody srovnávacího testu:
Pomocí StringTokenizer:
StringBuilder buffer = new(); var tokenizer = new StringTokenizer( s_nineHundredAutoGeneratedParagraphsOfLoremIpsum, new[] { ' ', '.' }); foreach (StringSegment segment in tokenizer) { buffer.Append(segment.Value); }
Pomocí String.Split:
StringBuilder buffer = new(); string[] tokenizer = s_nineHundredAutoGeneratedParagraphsOfLoremIpsum.Split( new[] { ' ', '.' }); foreach (string segment in tokenizer) { buffer.Append(segment); }
Obě metody vypadají podobně v oblasti rozhraní API a oba dokážou rozdělit velký řetězec na bloky dat. Níže uvedené výsledky srovnávacího testu ukazují, že StringTokenizer
přístup je téměř třikrát rychlejší, ale výsledky se mohou lišit. Stejně jako u všech aspektů výkonu byste měli vyhodnotit konkrétní případ použití.
metoda | Střední hodnota | Chyba | Směrodatná odchylka | Koeficient |
---|---|---|---|---|
Tokenizátor | 3.315 ms | 0.0659 ms | 0.0705 ms | 0.32 |
Rozděleno | 10.257 ms | 0.2018 ms | 0.2552 ms | 1.00 |
Legenda
- Střední hodnota: Aritmetický průměr všech měření
- Chyba: Polovina intervalu spolehlivosti 99,9 %
- Směrodatná odchylka: Směrodatná odchylka všech měření
- Medián: Hodnota oddělující vyšší polovinu všech měření (50. percentil)
- Poměr: Střední hodnota rozdělení poměru (aktuální/směrný plán)
- Směrodatná odchylka poměru: Směrodatná odchylka rozdělení poměru (aktuální/směrný plán)
- 1 ms: 1 Milisekunda (0,001 s)
Další informace o srovnávacích testech pomocí .NET najdete v tématu BenchmarkDotNet.
Typ StringValues
Objekt StringValues je struct
typ, který představuje null
, nula, jeden nebo mnoho řetězců efektivním způsobem. Typ StringValues
lze vytvořit pomocí některé z následujících syntaxí: string?
nebo string?[]?
. Při použití textu z předchozího příkladu zvažte následující kód jazyka C#:
StringValues values =
new(s_nineHundredAutoGeneratedParagraphsOfLoremIpsum.Split(
new[] { '\n' }));
Console.WriteLine($"Count = {values.Count:#,#}");
foreach (string? value in values)
{
// Interact with the value
}
// Outputs:
// Count = 1,799
Předchozí kód vytvoří instanci objektu StringValues
s polem řetězcových hodnot. Zapisuje se StringValues.Count do konzoly.
Typ StringValues
je implementace následujících typů kolekcí:
IList<string>
ICollection<string>
IEnumerable<string>
IEnumerable
IReadOnlyList<string>
IReadOnlyCollection<string>
V takovém případě se dá itestrovat a každý z nich value
se dá podle potřeby interagovat.