Megosztás a következőn keresztül:


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:

É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érdeznie HasChanged 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 RegisterChangeCallbackalternatí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 egy string példányt.
  • StringValues: Hatékonyan jelöli nulla 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 StringSegmentminden 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:

  1. 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);
    }
    
  2. 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 nullnullá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ó.

Lásd még