基本類型:.NET 的延伸模組程式庫
在本文中,您將瞭解 Microsoft.Extensions.Primitives 程式庫。 請勿將本文所述的基本類型與 BCL 或 C# 語言的 .NET 基本類型混淆。 相反地,基本類型程式庫中的類型是某些周邊 .NET NuGet 套件的基礎,例如:
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
變更通知
變更發生時傳播通知是程式設計的基本概念。 我們所觀察到的物件狀態通常都會變化。 發生變更時,Microsoft.Extensions.Primitives.IChangeToken 介面的實作可用來通知相關人士上述變更。 可用的實作項目如下:
身為開發人員,您也可以自由實作自己的類型。 介面 IChangeToken 會定義一些屬性:
- IChangeToken.HasChanged:取得代表是否已發生變更的值。
- IChangeToken.ActiveChangeCallbacks:指出此權杖是否將主動引發回撥。 若為
false
,則權杖取用者必須輪詢HasChanged
來偵測變更。
以執行個體為基礎的功能
請考慮以下 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
值、offset
和 length
具現化 StringSegment
。 StringSegment.Buffer 是原始字串引數,而 StringSegment.Value 是以 StringSegment.Offset 和 StringSegment.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
互動。
比較 StringTokenizer
與 string.Split
的基準測試
由於切割字串的方式十分多樣化,所以比較兩種方法時與基準比較更為合適。 使用 BenchmarkDotNet NuGet 套件時,請考量下列兩個基準測試方法:
使用 StringTokenizer:
StringBuilder buffer = new(); var tokenizer = new StringTokenizer( s_nineHundredAutoGeneratedParagraphsOfLoremIpsum, new[] { ' ', '.' }); foreach (StringSegment segment in tokenizer) { buffer.Append(segment.Value); }
使用 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
互動。