Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
In dit artikel vindt u informatie over de bibliotheek Microsoft.Extensions.Primitives . De primitieven in dit artikel zijn niet te verwarren met .NET primitieve typen uit de BCL of die van de C#-taal. In plaats daarvan fungeren de typen binnen de primitieve bibliotheek als bouwstenen voor enkele secundaire .NET NuGet-pakketten, zoals:
Microsoft.Extensions.ConfigurationMicrosoft.Extensions.Configuration.FileExtensionsMicrosoft.Extensions.FileProviders.CompositeMicrosoft.Extensions.FileProviders.PhysicalMicrosoft.Extensions.Logging.EventSourceMicrosoft.Extensions.OptionsSystem.Text.Json
Wijzigingsmeldingen
Meldingen doorgeven wanneer een wijziging plaatsvindt, is een fundamenteel concept in programmeren. De waargenomen toestand van een object kan vaker dan niet worden gewijzigd. Wanneer er veranderingen optreden, kunnen implementaties van de interface worden gebruikt om belanghebbenden op de Microsoft.Extensions.Primitives.IChangeToken hoogte te stellen van deze wijziging. De beschikbare implementaties zijn als volgt:
Als ontwikkelaar kunt u ook uw eigen type implementeren. De IChangeToken interface definieert enkele eigenschappen:
- IChangeToken.HasChanged: Haalt een waarde op die aangeeft of er een wijziging is opgetreden.
-
IChangeToken.ActiveChangeCallbacks: Geeft aan of het token proactief callbacks genereert. Als
false, moet de token-consumentHasChangedpollen om wijzigingen te detecteren.
Functionaliteit op basis van instanties
Bekijk het volgende voorbeeldgebruik van: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
In het voorgaande voorbeeld wordt een CancellationTokenSource-object gemaakt en wordt Token doorgegeven aan de CancellationChangeToken-constructor. De initiële toestand van HasChanged wordt naar de console geschreven. Er wordt een Action<object?> callback aangemaakt die naar de console schrijft wanneer de callback wordt aangeroepen. De methode van het token RegisterChangeCallback(Action<Object>, Object) wordt aangeroepen, gegeven de callback. Binnen de using verklaring wordt de cancellationTokenSource bewerking geannuleerd. Hiermee wordt de callback geactiveerd en wordt de status HasChanged opnieuw naar de console geschreven.
Wanneer u actie moet ondernemen vanuit meerdere wijzigingsbronnen, gebruikt u de CompositeChangeToken. Met deze implementatie worden een of meer wijzigingstokens samengevoegd en wordt elke geregistreerde callback precies één keer geactiveerd, ongeacht het aantal keren dat een wijziging wordt geactiveerd. Kijk een naar het volgende voorbeeld:
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.
In de voorgaande C#-code worden drie CancellationTokenSource objectexemplaren gemaakt en gekoppeld aan bijbehorende CancellationChangeToken exemplaren. Het samengestelde token wordt geïnstantieerd door een matrix van de tokens door te geven aan de CompositeChangeToken constructor. Het Action<object?> callback-object wordt gemaakt, maar deze keer wordt het state-object gebruikt en geschreven naar de console als een opgemaakt bericht. De callback wordt vier keer geregistreerd, elk met een iets ander statusobjectargument. De code maakt gebruik van een pseudo-willekeurige getalgenerator om een van de wijzigingstokenbronnen te kiezen (maakt niet uit welke) en roept de methode aan Cancel() . Hierdoor wordt de wijziging geactiveerd, waarbij elke geregistreerde callback precies één keer wordt aangeroepen.
Houd er rekening mee dat ActiveChangeCallbacks van de CompositeChangeTokentrue is wanneer ten minste één intern token actieve callbacks ondersteunt. Het samengestelde token abonneert zich alleen op die actieve binnenste tokens. Als u een wijziging in een passief intern token wilt detecteren, bevraagt u periodiek de eigenschap HasChanged van de samengestelde token.
Alternatieve static benadering
Als alternatief voor aanroepen RegisterChangeCallbackkunt u de Microsoft.Extensions.Primitives.ChangeToken statische klasse gebruiken. Houd rekening met het volgende verbruikspatroon:
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.
Net als in eerdere voorbeelden hebt u een IChangeToken implementatie nodig die wordt geretourneerd door changeTokenProducer. De producent is een Func<IChangeToken?>. Na een wijziging moet er een nieuw token worden geretourneerd voor de volgende registratie. Als dit null retourneert, registreert ChangeToken.OnChange geen callback. Geef de door OnChange geretourneerde IDisposable vrij om de consumer af te melden.
Geef voor een synchrone consumer een Action<TState> of Action door. Uitzonderingen van de producent of consument worden doorgegeven aan de aanroeper die het token registreert of activeert.
Vanaf .NET 11 kunt u voor een asynchrone gebruiker een Func<Task> of Func<TState, Task> doorgeven.
ChangeToken.OnChange wacht totdat de geretourneerde taak is voltooid voordat de consument opnieuw kan worden aangeroepen. Dit gedrag voorkomt gelijktijdige consumentenaanroepen, maar het kan meerdere wijzigingen combineren die optreden terwijl de taak in één latere callback wordt uitgevoerd. Een uitzondering die is gegenereerd voordat de consument de taak retourneert, wordt doorgegeven aan de code waarmee het token wordt geregistreerd of geactiveerd. Uitzonderingen die optreden nadat de taak wordt geretourneerd, blijven ongeobserveerd door ChangeToken.OnChange.
Opmerking
Wanneer u bestaande code opnieuw compileert voor .NET 11, kan een async lambda worden gekoppeld aan een nieuwe task-retournerende overbelasting in plaats van aan de synchrone Action-overbelasting. De nieuwe binding wijzigt de tijdsinstellingen voor terugbellen van async void gedrag naar gedrag op basis van taken. Zie ChangeToken.OnChange async overloads rebind existing Task-returning callbacks voor meer informatie.
Tekenreeks-tokenizers, segmenten en waarden
Interactie met tekenreeksen is gebruikelijk bij het ontwikkelen van toepassingen. Verschillende weergaven van tekenreeksen worden geparseerd, gesplitst of opnieuw bekeken. De primitive-bibliotheek biedt een aantal keuzetypen die helpen om de interactie met strings geoptimaliseerd en efficiënter te maken. Houd rekening met de volgende typen:
- StringSegment: Een geoptimaliseerde weergave van een subtekenreeks.
-
StringTokenizer: Tokeniseert een
stringtotStringSegmentinstanties. -
StringValues: Vertegenwoordigt
null, nul, één of veel tekenreeksen op een efficiënte manier.
Het StringSegment type
In deze sectie leert u meer over een geoptimaliseerde weergave van een subtekenreeks die het type wordt genoemd StringSegmentstruct . Bekijk het volgende C#-codevoorbeeld met enkele eigenschappen StringSegment en de AsSpan methode:
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 "
De voorgaande code maakt een instantie van de StringSegment met een string-waarde, een offset en een length. Dit StringSegment.Buffer is het oorspronkelijke tekenreeksargument en de StringSegment.Value subtekenreeks is gebaseerd op de StringSegment.Offset en StringSegment.Length waarden.
De StringSegment struct biedt veel methoden voor interactie met het segment.
Het StringTokenizer type
Het StringTokenizer object is een structtype dat een string tokenizet in StringSegment exemplaren. De tokenisatie van grote tekenreeksen omvat meestal het splitsen van de tekenreeks en het herhalen ervan. Dat gezegd hebbende, String.Split komt waarschijnlijk in gedachten. Deze API's zijn vergelijkbaar, maar in het algemeen StringTokenizer bieden ze betere prestaties. Bekijk eerst het volgende voorbeeld:
var tokenizer =
new StringTokenizer(
s_nineHundredAutoGeneratedParagraphsOfLoremIpsum,
new[] { ' ' });
foreach (StringSegment segment in tokenizer)
{
// Interact with segment
}
In de voorgaande code wordt een exemplaar van het StringTokenizer type gemaakt met 900 automatisch gegenereerde alinea's met Lorem Ipsum tekst en een matrix met één waarde van een witruimteteken ' '. Elke waarde binnen de tokenizer wordt weergegeven als een StringSegment. De code doorloopt de segmenten, zodat de consument met elk segmentsegment kan communiceren.
Benchmark van StringTokenizer vergeleken met string.Split
Met de verschillende manieren om tekenreeksen te segmenteren en te splitsen, is het gepast om twee methoden te vergelijken met een benchmark. Houd rekening met de volgende twee benchmarkmethoden met behulp van het NuGet-pakket BenchmarkDotNet :
Met behulp van StringTokenizer:
StringBuilder buffer = new(); var tokenizer = new StringTokenizer( s_nineHundredAutoGeneratedParagraphsOfLoremIpsum, new[] { ' ', '.' }); foreach (StringSegment segment in tokenizer) { buffer.Append(segment.Value); }Met behulp van String.Split:
StringBuilder buffer = new(); string[] tokenizer = s_nineHundredAutoGeneratedParagraphsOfLoremIpsum.Split( new[] { ' ', '.' }); foreach (string segment in tokenizer) { buffer.Append(segment); }
Beide methoden lijken qua functionaliteit op het API-oppervlak op elkaar en ze zijn beide in staat om een grote tekenreeks in segmenten te splitsen. In de onderstaande benchmarkresultaten ziet u dat de StringTokenizer aanpak bijna drie keer sneller is, maar de resultaten kunnen variëren. Net als bij alle prestatieoverwegingen moet u uw specifieke use case evalueren.
| Wijze | Gemiddelde | Fout | StdDev | Verhouding |
|---|---|---|---|---|
| Tokenizer | 3.315 ms | 0.0659 ms | 0.0705 ms | 0.32 |
| Splijten | 10.257 ms | 0.2018 ms | 0,2552 ms | 1,00 |
Legenda
- Gemiddelde: Rekenkundig gemiddelde van alle metingen
- Fout: de helft van het betrouwbaarheidsinterval van 99,9%
- Standaarddeviatie: Standaarddeviatie van alle metingen
- Mediaan: Waarde die de hogere helft van alle metingen scheidt (50e percentiel)
- Verhouding: gemiddelde van de verhoudingsverdeling (huidige/basislijn)
- Verhouding standaarddeviatie: Standaarddeviatie van de verhoudingsverdeling (huidige situatie/basislijn)
- 1 ms: 1 milliseconden (0,001 sec)
Zie BenchmarkDotNet voor meer informatie over benchmarking met .NET.
Het StringValues type
Het StringValues object is een struct type dat op een efficiënte manier nul, één of veel tekenreeksen vertegenwoordigt null. Het StringValues type kan worden samengesteld met een van de volgende syntaxis: string? of string?[]?. Houd rekening met de volgende C#-code met behulp van de tekst uit het vorige voorbeeld:
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
Met de voorgaande code wordt een StringValues object geïnstitueert op basis van een matrix met tekenreekswaarden. De StringValues.Count wordt naar de console geschreven.
Het StringValues type is een implementatie van de volgende verzamelingstypen:
IList<string>ICollection<string>IEnumerable<string>IEnumerableIReadOnlyList<string>IReadOnlyCollection<string>
Als zodanig kan het worden geïtereerd en kan met elk value worden omgegaan indien nodig.