共用方式為


基本類型:.NET 的延伸模組程式庫

在本文中,您將瞭解 Microsoft.Extensions.Primitives 程式庫。 請勿將本文所述的基本類型與 BCL 或 C# 語言的 .NET 基本類型混淆。 相反地,基本類型程式庫中的類型是某些周邊 .NET NuGet 套件的基礎,例如:

變更通知

變更發生時傳播通知是程式設計的基本概念。 我們所觀察到的物件狀態通常都會變化。 發生變更時,Microsoft.Extensions.Primitives.IChangeToken 介面的實作可用來通知相關人士上述變更。 可用的實作項目如下:

身為開發人員,您也可以自由實作自己的類型。 介面 IChangeToken 會定義一些屬性:

以執行個體為基礎的功能

請考慮以下 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

在上述範例中,CancellationTokenSource 會具現化並將其 Token 傳遞至 CancellationChangeToken 建構函式。 HasChanged 的初始狀態會寫入主控台。 建立 Action<object?> callback 以在主控台叫用回撥時寫入。 根據 callback 呼叫權杖的 RegisterChangeCallback(Action<Object>, Object) 方法。 在 using 陳述式中,cancellationTokenSource 被取消。 這會觸發回撥,而 HasChanged 的狀態會再次寫入主控台。

當您需要從多個變更來源採取動作時,請使用 CompositeChangeToken。 此實作會匯總一或多個變更權杖,然後無論觸發變更幾次,都只引發每個已註冊的回撥一次。 請考慮下列範例:

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.

上述 C# 程式碼建立了三個 CancellationTokenSource 物件執行個體,並與對應的 CancellationChangeToken 執行個體進行配對。 複合標記會藉由將權杖的陣列傳遞至 CompositeChangeToken 建構函式來具現化。 程式會建立 Action<object?> callback,但這次會使用 state 物件並做為格式化訊息寫入主控台。 回撥會註冊四次,每次的狀態物件引數都稍微不同。 此程式碼會使用偽隨機數產生器來選擇一個變更權杖來源 (哪一個都可以),然後呼叫其 Cancel() 方法。 這會觸發變更,並叫用每個已註冊的回撥一次。

替代 static 方法

除了呼叫 RegisterChangeCallback 以外,您也可以使用靜態類別 Microsoft.Extensions.Primitives.ChangeToken。 請考慮以下耗用模式:

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.

和先前的範例一樣,您需要 changeTokenProducer 產生的 IChangeToken 實作。 產生者會定義為 Func<IChangeToken>,預計每次叫用會傳回一個新的權杖。 consumer 在不使用 state 時是 Action,在泛型型別 TState 流經變更通知時則為 Action<TState>

字串權杖化工具、區段和值

字串互動在應用程式開發中很常見。 字串的各種表示會受到剖析、分割或逐一查看。 基本類型程式庫提供幾種可選擇的類型,可讓與字串的互動最佳化且更有效率。 請考慮以下類型:

  • StringSegment:子字串的最佳化表示法。
  • StringTokenizer:將 string 權杖化為 StringSegment 執行個體。
  • StringValues:以有效率的方式代表 null、零、一或多個字串。

StringSegment 類型

在本節中,您將瞭解名為 StringSegment struct 類型的子字串最佳化標記法。 請考慮以下顯示某些 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 @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 "

上述程式碼會根據給定的 string 值、offsetlength 具現化 StringSegmentStringSegment.Buffer 是原始字串引數,而 StringSegment.Value 是以 StringSegment.OffsetStringSegment.Length 值為基礎的子字串。

結構 StringSegment 提供許多方法與區段互動。

StringTokenizer 類型

物件 StringTokenizer 是一種結構類型,會將 string 權杖化為 StringSegment 執行個體。 大型字串的標記化通常需要將字串分割並逐一查看。 說到這裡,您可能會想到 String.Split。 這些 API 很類似,但一般而言,StringTokenizer 的效能更佳。 首先,請考慮以下範例:

var tokenizer =
    new StringTokenizer(
        s_nineHundredAutoGeneratedParagraphsOfLoremIpsum,
        new[] { ' ' });

foreach (StringSegment segment in tokenizer)
{
    // Interact with segment
}

上述程式碼根據給定的 900 個自動產生的 Lorem Ipsum 文字段落和一個具有單一空白字元 ' ' 值的陣列,建立了 StringTokenizer 類型的執行個體。 權杖化工具中的每個值都會以 StringSegment 表示。 此程式碼會逐一查看各區段,讓取用者能與每個 segment 互動。

比較 StringTokenizerstring.Split的基準測試

由於切割字串的方式十分多樣化,所以比較兩種方法時與基準比較更為合適。 使用 BenchmarkDotNet NuGet 套件時,請考量下列兩個基準測試方法:

  1. 使用 StringTokenizer

    StringBuilder buffer = new();
    
    var tokenizer =
        new StringTokenizer(
            s_nineHundredAutoGeneratedParagraphsOfLoremIpsum,
            new[] { ' ', '.' });
    
    foreach (StringSegment segment in tokenizer)
    {
        buffer.Append(segment.Value);
    }
    
  2. 使用 String.Split

    StringBuilder buffer = new();
    
    string[] tokenizer =
        s_nineHundredAutoGeneratedParagraphsOfLoremIpsum.Split(
            new[] { ' ', '.' });
    
    foreach (string segment in tokenizer)
    {
        buffer.Append(segment);
    }
    

這兩種方法在 API 介面區上看起來很類似,而且兩者都能夠將大型字串分割成區塊。 下列基準測試結果顯示 StringTokenizer 方法快將近三倍,但結果可能會有所不同。 如同所有效能考量因素,您也應該評估您具體的使用案例。

方法 平均數 錯誤 StdDev 比例
Tokenizer 3.315 ms 0.0659 ms 0.0705 ms 0.32
份額 10.257 ms 0.2018 ms 0.2552 ms 1.00

圖例

  • 平均值:所有量測值的算術平均數
  • 錯誤:99.9% 信賴區間的一半
  • 標準差:所有量測值的標準差
  • 中位數:分隔所有量測值中較高那一半的數值 (第 50 個百分位數)
  • 比率:比率分配的平均值 (目前/基準)
  • 比率標準差:比率分配的標準差 (目前/基準)
  • 1 ms:1 毫秒 (0.001 秒)

如需使用 .NET 進行基準測試的詳細資訊,請參閱 BenchmarkDotNet (英文)。

StringValues 類型

StringValues 物件是一種 struct 類型,可用有效率的方式表示 null、零、一或多個字串。 StringValues 類型可用下列語法之一來建構:string?string?[]?。 請考量下列 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

上述程式碼會根據給定的字串值陣列具現化 StringValues 物件。 StringValues.Count 會寫入主控台。

StringValues 類型是下列集合類型的實作:

  • IList<string>
  • ICollection<string>
  • IEnumerable<string>
  • IEnumerable
  • IReadOnlyList<string>
  • IReadOnlyCollection<string>

因此,它可以逐一查看,並視需要與每個 value 互動。

另請參閱