Коллекции
Среда выполнения .NET предоставляет множество типов коллекций, которые хранят группы связанных объектов и управляют ими. Некоторые типы коллекций, такие как System.ArraySystem.Span<T>, и System.Memory<T> распознаются на языке C#. Кроме того, интерфейсы, такие как System.Collections.Generic.IEnumerable<T> распознаются на языке для перечисления элементов коллекции.
Коллекции предоставляют гибкий способ работы с группами объектов. Вы можете классифицировать различные коллекции по следующим характеристикам:
- Доступ к элементам: каждая коллекция может быть перечислена для доступа к каждому элементу в порядке. Некоторые коллекции обращаются к элементам по индексу, позиция элемента в упорядоченной коллекции. Наиболее распространенным примером является System.Collections.Generic.List<T>. Другие коллекции обращаются к элементам по ключу, где значение связано с одним ключом. Наиболее распространенным примером является System.Collections.Generic.Dictionary<TKey,TValue>. Вы выбираете между этими типами коллекций в зависимости от способа доступа к элементам приложения.
- Профиль производительности. Каждая коллекция имеет различные профили производительности для действий, таких как добавление элемента, поиск элемента или удаление элемента. Вы можете выбрать тип коллекции на основе операций, используемых большинством в приложении.
- Динамическое увеличение и сжатие: большинство коллекций поддерживают динамическое добавление или удаление элементов. В частности, Array, System.Span<T>и System.Memory<T> не.
Помимо этих характеристик среда выполнения предоставляет специализированные коллекции, которые препятствуют добавлению или удалению элементов или изменению элементов коллекции. Другие специализированные коллекции обеспечивают безопасность параллельного доступа в многопоточных приложениях.
Все типы коллекций можно найти в справочнике по API .NET. Дополнительные сведения см. в разделе "Часто используемые типы коллекций" и выбор класса коллекции.
Примечание.
В примерах этой статьи может потребоваться добавить директивы using для System.Collections.Generic
пространств имен и System.Linq
пространств имен.
Массивы представлены System.Array и поддерживают синтаксис на языке C#. Этот синтаксис предоставляет более краткие объявления для переменных массива.
System.Span<T>ref struct
— это тип, предоставляющий моментальный снимок по последовательности элементов без копирования этих элементов. Компилятор применяет правила безопасности, чтобы убедиться Span
, что доступ к ней невозможно получить после того, как последовательность, на которую она ссылается, больше не находится в области. Он используется во многих API .NET для повышения производительности. Memory<T> обеспечивает аналогичное поведение, если не удается использовать ref struct
тип.
Начиная с C# 12, все типы коллекций можно инициализировать с помощью выражения Collection.
Индексируемые коллекции
Индексируемая коллекция — это коллекция , в которой можно получить доступ к каждому элементу с помощью его индекса. Его индекс — это количество элементов перед ним в последовательности. Таким образом, ссылка на элемент по индексу 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
В следующем примере элементы из списка удаляются по индексу. Вместо инструкции foreach
используется for
оператор, который выполняет итерацию в порядке убывания. Метод 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> можно также определить собственный класс. В приведенном ниже примере класс Galaxy
, который используется объектом List<T>, определен в коде.
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> класс. Это самая распространенная коллекция словарей. Коллекция словарей позволяет получить доступ к элементам в коллекции с помощью ключа каждого элемента. Каждый элемент, добавляемый в словарь, состоит из значения и связанного с ним ключа.
В приведенном ниже примере создается коллекция Dictionary
и выполняется перебор словаря с помощью оператора foreach
.
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 и свойство Item[]Dictionary
для быстрого поиска элемента по ключу. Свойство Item
позволяет получить доступ к элементу в коллекции elements
с помощью кода elements[symbol]
в C#.
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#).
В приведенном ниже примере используется метод-итератор. Метод итератора содержит yield return
инструкцию, которая находится внутри for
цикла. В методе ListEvenNumbers
каждая итерация тела оператора foreach
создает вызов метода-итератора, который переходит к следующему оператору 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 и коллекции
Для доступа к коллекциям можно использовать интегрированный с языком запрос (LINQ). Запросы LINQ обеспечивают возможности фильтрации, упорядочения и группировки. Дополнительные сведения см. в разделе Приступая к работе с LINQ в C#.
В приведенном ниже примере выполняется запрос LINQ применительно к универсальной коллекции List
. Запрос 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}}
};