Primitívek: A .NET bővítménytára
Ebben a cikkben megismerheti a Microsoft.Extensions.Primitives kódtárat . A cikkben szereplő primitívek nem tévesztendők össze a BCL-ből származó .NET primitív típusokkal vagy a C# nyelvével. Ehelyett a primitív kódtárban lévő típusok a periféria .NET NuGet-csomagjainak építőelemei, például:
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
Értesítések módosítása
Az értesítések propagálása változás esetén a programozás alapvető fogalma. Az objektumok megfigyelt állapota gyakrabban változik, mintsem nem. Ha változás történik, a felület implementációi felhasználhatók az Microsoft.Extensions.Primitives.IChangeToken érdekelt felek értesítésére az említett változásról. Az elérhető implementációk a következők:
Fejlesztőként ön is szabadon implementálhatja saját típusát. A IChangeToken felület néhány tulajdonságot határoz meg:
- IChangeToken.HasChanged: Olyan értéket kap, amely jelzi, hogy történt-e változás.
- IChangeToken.ActiveChangeCallbacks: Azt jelzi, hogy a jogkivonat proaktív módon hívja-e fel a visszahívásokat. Ha
false
, a jogkivonat-fogyasztónak le kell kérdeznieHasChanged
a módosításokat.
Példányalapú funkciók
Vegye figyelembe a következő példa használatát: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
Az előző példában CancellationTokenSource egy példányosított Token példányt ad át a CancellationChangeToken konstruktornak. A rendszer a kezdeti állapotot HasChanged
a konzolra írja. Létrejön egy Action<object?> callback
írás, amely a visszahívás konzolra való meghívásakor jön létre. A jogkivonat metódusának meghívása RegisterChangeCallback(Action<Object>, Object) a callback
. A nyilatkozaton using
belül a cancellationTokenSource
törlést visszavonjuk. Ez aktiválja a visszahívást, és az állapot HasChanged
ismét meg lesz írva a konzolra.
Ha több változási forrásból kell műveletet elvégeznie, használja a CompositeChangeToken. Ez a megvalósítás egy vagy több változási jogkivonatot összesít, és minden regisztrált visszahívást pontosan egy alkalommal indít el, függetlenül attól, hogy a módosítás hányszor aktiválódik. Vegyük a következő példát:
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.
Az előző C#-kódban három CancellationTokenSource objektumpéldány jön létre és párosítva a megfelelő CancellationChangeToken példányokkal. Az összetett jogkivonat példányosítása a jogkivonatok tömbjének a konstruktornak CompositeChangeToken való átadásával történik. A Action<object?> callback
rendszer létrehozza az objektumot, de ezúttal formázott state
üzenetként használja és írja a konzolra. A visszahívás négy alkalommal van regisztrálva, mindegyikhez egy kissé eltérő állapotobjektum-argumentum tartozik. A kód egy pszeudo-véletlenszerű számgenerátor használatával választja ki a változási jogkivonat forrásainak egyikét (nem számít, melyiket), és meghívja annak metódusát Cancel() . Ez aktiválja a módosítást, és minden regisztrált visszahívást pontosan egyszer hív meg.
Alternatív static
megközelítés
A hívás RegisterChangeCallback
alternatívaként használhatja a statikus osztályt Microsoft.Extensions.Primitives.ChangeToken . Vegye figyelembe a következő fogyasztási mintát:
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.
A korábbi példákhoz hasonlóan szüksége lesz egy olyan implementációra IChangeToken
, amelyet a changeTokenProducer
. A gyártó definíciója Func<IChangeToken>
egy, és várhatóan minden meghíváskor egy új jogkivonatot ad vissza. Ez consumer
lehet egy Action
olyan, amikor nem használ state
, vagy egy Action<TState>
olyan, ahol az általános típus TState
áthalad a változásértesítésen.
Sztring jogkivonat-szabályzók, szegmensek és értékek
A sztringekkel való interakció gyakori az alkalmazásfejlesztésben. A sztringek különböző ábrázolásai elemezhetők, feloszthatók vagy iterálhatók. A primitívek kódtára kínál néhány választható típust, amelyek segítenek optimalizáltabbá és hatékonyabbá tenni a sztringekkel való interakciót. Vegye figyelembe a következő típusokat:
- StringSegment: Egy részszűkítés optimalizált ábrázolása.
- StringTokenizer: Jogkivonatokká alakít
StringSegment
egystring
példányt. - StringValues: Hatékonyan jelöli
null
a nulla, egy vagy több sztringet.
A StringSegment
típus
Ebben a szakaszban megismerheti a típusként StringSegment struct
ismert alsztringek optimalizált ábrázolását. Tekintse meg a következő C#-kód példáját, amely néhány StringSegment
tulajdonságot és metódust AsSpan
mutat be:
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 "
Az előző kód a megadott értéket, egy string
offset
és egy length
értéket példányosítStringSegment
. Az StringSegment.Buffer eredeti sztringargumentum, a StringSegment.Value részsztring pedig az és StringSegment.Length az StringSegment.Offset értékek alapján.
A StringSegment
szerkezet számos módszert kínál a szegmensekkel való interakcióhoz.
A StringTokenizer
típus
Az StringTokenizer objektum egy olyan struktúratípus, amely tokenizál egy string
példányt StringSegment
. A nagy sztringek jogkivonatos létrehozása általában magában foglalja a sztring szétosztását és iterálását. Ha ez így van, String.Split valószínűleg eszembe jut. Ezek az API-k hasonlóak, de általában StringTokenizer jobb teljesítményt nyújtanak. Először vegye figyelembe a következő példát:
var tokenizer =
new StringTokenizer(
s_nineHundredAutoGeneratedParagraphsOfLoremIpsum,
new[] { ' ' });
foreach (StringSegment segment in tokenizer)
{
// Interact with segment
}
Az előző kódban a StringTokenizer
rendszer 900 automatikusan létrehozott szöveg bekezdést Lorem Ipsum és egy szóköz karaktert ' '
tartalmazó tömböt hoz létre. A tokenizer StringSegment
minden egyes értéke egy . A kód iterálja a szegmenseket, lehetővé téve a fogyasztó számára az egyes segment
.
Összehasonlítás a StringTokenizer
teljesítménymutatóval string.Split
A sztringek szeletelésének és diktálásának különböző módjaival célszerű két módszert hasonlítani egy viszonyítási ponthoz. A BenchmarkDotNet NuGet-csomag használatával fontolja meg a következő két benchmark metódust:
A következő használatával StringTokenizer:
StringBuilder buffer = new(); var tokenizer = new StringTokenizer( s_nineHundredAutoGeneratedParagraphsOfLoremIpsum, new[] { ' ', '.' }); foreach (StringSegment segment in tokenizer) { buffer.Append(segment.Value); }
A következő használatával String.Split:
StringBuilder buffer = new(); string[] tokenizer = s_nineHundredAutoGeneratedParagraphsOfLoremIpsum.Split( new[] { ' ', '.' }); foreach (string segment in tokenizer) { buffer.Append(segment); }
Mindkét metódus hasonlóan néz ki az API felületén, és mindkettő képes nagy sztringeket darabkákra osztani. Az alábbi teljesítményteszt eredményei azt mutatják, hogy a StringTokenizer
megközelítés közel háromszor gyorsabb, de az eredmények eltérőek lehetnek. Mint minden teljesítménybeli szempontnál, a konkrét használati esetet is ki kell értékelnie.
Metódus | Középérték | Hiba | StdDev | Arány |
---|---|---|---|---|
Tokenizer | 3,315 ms | 0,0659 ms | 0,0705 ms | 0.32 |
Split | 10,257 ms | 0,2018 ms | 0,2552 ms | 1,00 |
Legenda
- Középérték: Az összes mérés számtani középértéke
- Hiba: A 99,9%-os megbízhatósági intervallum fele
- Szórás: Az összes mérés szórása
- Medián: Az összes mérés felső felét elválasztó érték (50. percentilis)
- Arány: Az arányeloszlás középértéke (aktuális/alapkonfiguráció)
- Arány szórása: Az arányeloszlás szórása (aktuális/alapkonfiguráció)
- 1 ms: 1 ezredmásodperc (0,001 mp)
A .NET-hez való teljesítménytesztről további információt a BenchmarkDotNetben talál.
A StringValues
típus
Az StringValues objektum olyan struct
típus, amely hatékonyan jelöl null
nullát, egy vagy több sztringet. A StringValues
típus a következő szintaxisok egyikével hozható létre: string?
vagy string?[]?
. Az előző példa szövegét használva vegye figyelembe a következő C#-kódot:
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
Az előző kód egy sztringértékek tömbje alapján példányosít egy StringValues
objektumot. A StringValues.Count rendszer a konzolra írja.
A StringValues
típus a következő gyűjteménytípusok implementációja:
IList<string>
ICollection<string>
IEnumerable<string>
IEnumerable
IReadOnlyList<string>
IReadOnlyCollection<string>
Ennek megfelelően át lehet iterálni, és mindegyik value
szükség szerint használható.