共用方式為


集合

.NET 執行階段提供許多集合類型,可儲存和管理相關物件的群組。 C# 語言可以辨識某些集合類型,例如 System.ArraySystem.Span<T>System.Memory<T>。 此外,類似 System.Collections.Generic.IEnumerable<T> 的介面會以語言辨識,以列舉集合的元素。

集合會提供彈性的方式來使用物件群組。 您可以依照下列特性來分類不同的集合:

  • 元素存取:可以列舉每個集合以依序存取每個元素。 某些集合會依「索引」 即該元素在已排序的集合中的位置,存取元素。 最常見的範例為 System.Collections.Generic.List<T>。 其他集合會透過「索引鍵」存取元素,其中「值」與單一「索引鍵」相關聯。 最常見的範例為 System.Collections.Generic.Dictionary<TKey,TValue>。 您可以根據應用程式存取元素的方式,選擇這些集合類型。
  • 效能設定檔:每個集合都有不同的效能設定檔,可用於新增元素、尋找元素或移除元素等動作。 您可以根據應用程式中最常使用的作業來挑選集合類型。
  • 動態擴張和縮小:大部分集合支援動態新增或移除元素。 值得注意的是,ArraySystem.Span<T>System.Memory<T> 不支援。

除了這些特性之外,執行階段還提供特殊集合,以禁止新增或移除元素或修改集合的元素。 其他特殊集合為多執行緒應用程式中的並行存取提供安全性。

您可以在 .NET API 參考中找到所有集合類型。 如需詳細資訊,請參閱常用的集合類型 (部分機器翻譯) 和選取集合類型 (部分機器翻譯)。

注意

在本文章的範例中,您可能需要為 System.Collections.GenericSystem.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 方法和 DictionaryItem[] 屬性來依索引鍵快速尋找項目。 藉由使用 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}}
    };