您可以使用集合運算式來建立一般集合值。
集合運算式是一種 terse 語法,在評估時,可以指派給許多不同的集合型別。 集合運算式包含 [ 和 ] 方括弧之間的元素序列。 下列範例會宣告 System.Span<T> 元素的 string,並將其初始化為一週的日子:
Span<string> weekDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
foreach (var day in weekDays)
{
Console.WriteLine(day);
}
集合運算式可以轉換成許多不同的集合型別。 第一個範例示範如何使用集合運算式初始化變數。 下列程式碼顯示您可以使用集合運算式的其他許多位置:
// Initialize private field:
private static readonly ImmutableArray<string> _months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
// property with expression body:
public IEnumerable<int> MaxDays =>
[31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
public int Sum(IEnumerable<int> values) =>
values.Sum();
public void Example()
{
// As a parameter:
int sum = Sum([1, 2, 3, 4, 5]);
}
您無法在預期存在編譯時間常數時,使用集合運算式,例如初始化常數,或做為方法引述的預設值。
上述兩個範例都使用常數作為集合運算式的元素。 您也可以使用元素的變數,如下列範例所示:
string hydrogen = "H";
string helium = "He";
string lithium = "Li";
string beryllium = "Be";
string boron = "B";
string carbon = "C";
string nitrogen = "N";
string oxygen = "O";
string fluorine = "F";
string neon = "Ne";
string[] elements = [hydrogen, helium, lithium, beryllium, boron, carbon, nitrogen, oxygen, fluorine, neon];
foreach (var element in elements)
{
Console.WriteLine(element);
}
Spread 元素
您可以使用「擴張元素」.. 在集合運算式中內嵌集合值。 以下範例透過組合母音集合、子音集合和字母 "y" 來建立完整字母表的集合,而字母 "y" 可以是:
string[] vowels = ["a", "e", "i", "o", "u"];
string[] consonants = ["b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
"n", "p", "q", "r", "s", "t", "v", "w", "x", "z"];
string[] alphabet = [.. vowels, .. consonants, "y"];
評估時,spread 元素 ..vowels 會產生五個元素:"a"、"e"、"i"、"o" 和 "u"。 spread 元素 ..consonants 會產生 20 個元素,也就是 consonants 陣列中的數字。 必須使用 foreach 陳述式來列舉 spread 元素中的變數。 如上一個範例所示,您可以將 spread 元素與集合運算式中的個別元素結合。
轉換
集合運算式可以轉換成不同的集合型別,包含:
- System.Span<T> 和 System.ReadOnlySpan<T>。
-
陣列,例如
int[]或string[]。 - 具有參數型別為 的
ReadOnlySpan<T>方法的任何型別,其中存在從集合運算式型別到T的隱含轉換。 - 支援集合初始設定式的任何型別,例如 System.Collections.Generic.List<T>。 此需求通常表示型別支援 System.Collections.Generic.IEnumerable<T>,而且有一個可存取的
Add方法可將項目新增至集合。 必須有一個從集合運算式元素型別到集合元素型別的隱含轉換。 對於 spread 元素,必須有一個從 spread 元素型別轉換成集合元素型別的隱含轉換。 - 下列任何介面:
備註
集合運算式無法用來初始化 內嵌陣列。 內嵌陣列需要不同的初始化語法。
重要
集合運算式一律會建立包含集合運算式中所有元素的集合,不論轉換的目標類型為何。 例如,當轉換的目標為 System.Collections.Generic.IEnumerable<T> 時,產生的程式碼會評估集合運算式,並將結果儲存在記憶體內部集合中。
此行為與 LINQ 不同,在列舉序列之前,可能不會具現化序列。 您無法使用集合運算式來產生不會列舉的無限序列。
編譯器會使用靜態分析以確定最佳效能方法,以使用集合運算式建立宣告的集合。 例如,如果初始化後不修改目標,則空集合運算式 [] 可以實現為 Array.Empty<T>()。 當目標為 System.Span<T> 或 System.ReadOnlySpan<T> 時,儲存體可能會配置堆疊。
集合運算式功能規格會指定編譯器必須遵循的規則。
許多 API 會以多個集合型別作為參數來多載。 因為集合運算式可以轉換成許多不同的運算式類型,因此這些 API 可能需要在集合運算式上轉換,以指定正確的轉換。 下列轉換規則可解決一些模棱兩可的情況:
- 較佳的項目轉換優先於較佳的集合類型轉換。 換句話說,集合表達式中的項目類型比集合類型更重要。 這些功能規格會描述這些規則,以便 從集合表達式進行更好的轉換。
- 轉換為 Span<T>、ReadOnlySpan<T> 或其他
ref struct型別優於轉換為非 ref 結構型別。 - 轉換為非介面型別優於轉換為介面型別。
當集合運算式轉換成 Span 或 ReadOnlySpan 時,span 物件的安全內容會取自範圍中包含的所有元素的安全內容。 如需詳細規則,請參閱集合運算式規格 (部分機器翻譯)。
集合建立器
集合運算式會使用任何「表現良好」的集合類型。 表現良好的關聯具有下列特性:
-
Count集合上Length或 的值會產生與列舉時的元素數目相同的值。 - System.Collections.Generic 命名空間中的類型假設為無副作用。 因此,編譯器可以將這類類型可能用作中繼值 (否則不會公開) 的案例最佳化。
- 呼叫集合上某些適用的
.AddRange(x)成員,將會產生與逐一查看x相同的最終值,並將其所有列舉值個別新增至具有.Add的集合。
.NET 執行階段中的所有集合類型都表現良好。
警告
如果自訂集合類型的表現不佳,則不會定義當您使用該集合類型搭配集合運算式時的行為。
透過編寫 Create() 方法並對集合類型套用 System.Runtime.CompilerServices.CollectionBuilderAttribute 以指示建立器方法,您的類型可以選擇加入集合運算式支援。 例如,請考慮使用 80 個字元的固定長度緩衝區的應用程式。 該類別看起來類似下列程式碼:
public class LineBuffer : IEnumerable<char>
{
private readonly char[] _buffer;
private readonly int _count;
public LineBuffer(ReadOnlySpan<char> buffer)
{
_buffer = new char[buffer.Length];
_count = buffer.Length;
for (int i = 0; i < _count; i++)
{
_buffer[i] = buffer[i];
}
}
public int Count => _count;
public char this[int index]
{
get
{
if (index >= _count)
throw new IndexOutOfRangeException();
return _buffer[index];
}
}
public IEnumerator<char> GetEnumerator()
{
for (int i = 0; i < _count; i++)
{
yield return _buffer[i];
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
// etc
}
您希望將其與集合運算式搭配使用,如以下範例所示:
LineBuffer line = ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!'];
LineBuffer 型別會實作 IEnumerable<char>,因此編譯器會將它辨識為 char 項目的集合。 實作的 System.Collections.Generic.IEnumerable<T> 介面的型別參數表示元素型別。 您必須將兩個新增項目新增至應用程式,才能將集合運算式指派給 LineBuffer 物件。 首先,您必須建立包含 Create 方法的類別:
internal static class LineBufferBuilder
{
internal static LineBuffer Create(ReadOnlySpan<char> values) => new LineBuffer(values);
}
Create 方法必須傳回 LineBuffer 物件,而且必須採用型別 ReadOnlySpan<char> 的單一參數。
ReadOnlySpan 的型別參數必須符合集合的元素類型。 傳回泛型集合的建立器方法會將泛型 ReadOnlySpan<T> 當做其參數。 方法必須可存取與static。
最後,您必須將 CollectionBuilderAttribute 新增至 LineBuffer 類別宣告:
[CollectionBuilder(typeof(LineBufferBuilder), "Create")]
第一個參數會提供 Builder 類別的名稱。 第二個屬性會提供建立器方法的名稱。