集合
.NET 執行階段提供許多集合類型,可儲存和管理相關物件的群組。 C# 語言可以辨識某些集合類型,例如 System.Array、System.Span<T> 和 System.Memory<T>。 此外,類似 System.Collections.Generic.IEnumerable<T> 的介面會以語言辨識,以列舉集合的元素。
集合會提供彈性的方式來使用物件群組。 您可以依照下列特性來分類不同的集合:
- 元素存取:可以列舉每個集合以依序存取每個元素。 某些集合會依「索引」 即該元素在已排序的集合中的位置,存取元素。 最常見的範例為 System.Collections.Generic.List<T>。 其他集合會透過「索引鍵」存取元素,其中「值」與單一「索引鍵」相關聯。 最常見的範例為 System.Collections.Generic.Dictionary<TKey,TValue>。 您可以根據應用程式存取元素的方式,選擇這些集合類型。
- 效能設定檔:每個集合都有不同的效能設定檔,可用於新增元素、尋找元素或移除元素等動作。 您可以根據應用程式中最常使用的作業來挑選集合類型。
- 動態擴張和縮小:大部分集合支援動態新增或移除元素。 值得注意的是,Array、System.Span<T> 和 System.Memory<T> 不支援。
除了這些特性之外,執行階段還提供特殊集合,以禁止新增或移除元素或修改集合的元素。 其他特殊集合為多執行緒應用程式中的並行存取提供安全性。
您可以在 .NET API 參考中找到所有集合類型。 如需詳細資訊,請參閱常用的集合類型 (部分機器翻譯) 和選取集合類型 (部分機器翻譯)。
注意
在本文章的範例中,您可能需要為 System.Collections.Generic
和 System.Linq
命名空間新增 using directives。
陣列是由 System.Array 來表示,並且具有 C# 語言的語法支援。 此語法提供更簡潔的陣列變數宣告。
System.Span<T> 是一種 ref struct
類型,可在一連串元素上提供快照集,而不用複製這些元素。 編譯器會強制執行安全規則,以確保 Span
在它所參考的序列不再位於範圍內之後,無法存取。 它用於許多 .NET API 以改善效能。 當您無法使用 ref struct
類型時,Memory<T> 會提供類似的行為。
從 C# 12 開始,所有集合類型都可以使用集合運算式進行初始化。
可編製索引的集合
「可編製索引的集合」是您可以使用其索引來存取每個元素的集合。 其「索引」是序列中元素之前的元素數字。 因此,索引 0
所參考的元素是第一個元素,索引 1
是第二個元素,依此類推。 這些範例會使用 List<T> 類別。 這是最常見的可編製索引的集合。
下列範例會建立並初始化字串清單、移除元素,並將元素新增至清單結尾。 每次修改之後,它會使用 foreach 陳述式或 for
迴圈以反覆運算字串:
// Create a list of strings by using a
// collection initializer.
List<string> salmons = ["chinook", "coho", "pink", "sockeye"];
// Iterate through the list.
foreach (var salmon in salmons)
{
Console.Write(salmon + " ");
}
// Output: chinook coho pink sockeye
// Remove an element from the list by specifying
// the object.
salmons.Remove("coho");
// Iterate using the index:
for (var index = 0; index < salmons.Count; index++)
{
Console.Write(salmons[index] + " ");
}
// Output: chinook pink sockeye
// Add the removed element
salmons.Add("coho");
// Iterate through the list.
foreach (var salmon in salmons)
{
Console.Write(salmon + " ");
}
// Output: chinook pink sockeye coho
下列範例會依照索引移除清單中的元素。 它使用以遞減順序反覆運算的 for
陳述式,而不是 foreach
陳述式。 RemoveAt 方法會導致在已移除元素後的元素具有較低的索引值。
List<int> numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
// Remove odd numbers.
for (var index = numbers.Count - 1; index >= 0; index--)
{
if (numbers[index] % 2 == 1)
{
// Remove the element by specifying
// the zero-based index in the list.
numbers.RemoveAt(index);
}
}
// Iterate through the list.
// A lambda expression is placed in the ForEach method
// of the List(T) object.
numbers.ForEach(
number => Console.Write(number + " "));
// Output: 0 2 4 6 8
如需 List<T> 中的項目類型,您也可以定義自己的類別。 在下列範例中,List<T> 使用的 Galaxy
類別是在程式碼中定義的。
private static void IterateThroughList()
{
var theGalaxies = new List<Galaxy>
{
new (){ Name="Tadpole", MegaLightYears=400},
new (){ Name="Pinwheel", MegaLightYears=25},
new (){ Name="Milky Way", MegaLightYears=0},
new (){ Name="Andromeda", MegaLightYears=3}
};
foreach (Galaxy theGalaxy in theGalaxies)
{
Console.WriteLine(theGalaxy.Name + " " + theGalaxy.MegaLightYears);
}
// Output:
// Tadpole 400
// Pinwheel 25
// Milky Way 0
// Andromeda 3
}
public class Galaxy
{
public string Name { get; set; }
public int MegaLightYears { get; set; }
}
索引鍵/值組集合
這些範例會使用 Dictionary<TKey,TValue> 類別。 這是最常見的字典集合。 字典集合可讓您使用每個元素的索引鍵來存取集合中的元素。 加入字典中的每一個項目都是由值及其關聯索引鍵所組成。
下列範例會使用 foreach
陳述式建立 Dictionary
集合並逐一查看字典。
private static void IterateThruDictionary()
{
Dictionary<string, Element> elements = BuildDictionary();
foreach (KeyValuePair<string, Element> kvp in elements)
{
Element theElement = kvp.Value;
Console.WriteLine("key: " + kvp.Key);
Console.WriteLine("values: " + theElement.Symbol + " " +
theElement.Name + " " + theElement.AtomicNumber);
}
}
public class Element
{
public required string Symbol { get; init; }
public required string Name { get; init; }
public required int AtomicNumber { get; init; }
}
private static Dictionary<string, Element> BuildDictionary() =>
new ()
{
{"K",
new (){ Symbol="K", Name="Potassium", AtomicNumber=19}},
{"Ca",
new (){ Symbol="Ca", Name="Calcium", AtomicNumber=20}},
{"Sc",
new (){ Symbol="Sc", Name="Scandium", AtomicNumber=21}},
{"Ti",
new (){ Symbol="Ti", Name="Titanium", AtomicNumber=22}}
};
下列範例會使用 ContainsKey 方法和 Dictionary
的 Item[] 屬性來依索引鍵快速尋找項目。 藉由使用 C# 中的 elements[symbol]
,Item
屬性可讓您存取 elements
集合中的項目。
if (elements.ContainsKey(symbol) == false)
{
Console.WriteLine(symbol + " not found");
}
else
{
Element theElement = elements[symbol];
Console.WriteLine("found: " + theElement.Name);
}
下列範例會改用 TryGetValue 方法依索引鍵來快速尋找項目。
if (elements.TryGetValue(symbol, out Element? theElement) == false)
Console.WriteLine(symbol + " not found");
else
Console.WriteLine("found: " + theElement.Name);
迭代器
「迭代器」是用來在集合上執行自訂反覆項目。 迭代器可以是方法或 get
存取子。 迭代器會使用 yield return 陳述式,一次一個地傳回集合中的每個項目。
您會使用 foreach 陳述式來呼叫迭代器。 foreach
迴圈的每個反覆項目都會呼叫迭代器。 在迭代器中到達 yield return
陳述式時,會傳回運算式,並保留程式碼中的目前位置。 下一次呼叫迭代器時,便會從這個位置重新開始執行。
如需詳細資訊,請參閱迭代器 (C#)。
下列範例使用了 iterator 方法。 Iterator 方法具有 for
迴圈內的 yield return
陳述式。 在 ListEvenNumbers
方法中,foreach
陳述式主體的每個反覆項目都會建立對 Iterator 方法的呼叫,這個方法將繼續執行下一個 yield return
陳述式。
private static void ListEvenNumbers()
{
foreach (int number in EvenSequence(5, 18))
{
Console.Write(number.ToString() + " ");
}
Console.WriteLine();
// Output: 6 8 10 12 14 16 18
}
private static IEnumerable<int> EvenSequence(
int firstNumber, int lastNumber)
{
// Yield even numbers in the range.
for (var number = firstNumber; number <= lastNumber; number++)
{
if (number % 2 == 0)
{
yield return number;
}
}
}
LINQ 和集合
Language-Integrated Query (LINQ) 可用來存取集合。 LINQ 查詢提供篩選、排序和分組功能。 如需詳細資訊,請參閱開始使用 C# 中的 LINQ。
下列範例會對泛型 List
執行 LINQ 查詢。 LINQ 查詢會傳回包含結果的不同集合。
private static void ShowLINQ()
{
List<Element> elements = BuildList();
// LINQ Query.
var subset = from theElement in elements
where theElement.AtomicNumber < 22
orderby theElement.Name
select theElement;
foreach (Element theElement in subset)
{
Console.WriteLine(theElement.Name + " " + theElement.AtomicNumber);
}
// Output:
// Calcium 20
// Potassium 19
// Scandium 21
}
private static List<Element> BuildList() => new()
{
{ new(){ Symbol="K", Name="Potassium", AtomicNumber=19}},
{ new(){ Symbol="Ca", Name="Calcium", AtomicNumber=20}},
{ new(){ Symbol="Sc", Name="Scandium", AtomicNumber=21}},
{ new(){ Symbol="Ti", Name="Titanium", AtomicNumber=22}}
};