Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Разработчики используют универсальные шаблоны все время в .NET, будь то неявно или явно. Когда вы используете LINQ в .NET, вы когда-либо заметили, что вы работаете с IEnumerable<T>? Или если вы когда-либо видели в Интернете пример универсального репозитория для взаимодействия с базами данных с помощью Entity Framework, вы видели, что большинство методов возвращают IQueryable<T>
? Возможно, вы задались вопросом, что T находится в этих примерах и почему он там.
Впервые появившись в .NET Framework 2.0, универсальные шаблоны по сути являются "шаблоном кода", который позволяет разработчикам определять структуры данных, безопасные для типов , без фиксации фактического типа данных. Например, List<T> это универсальная коллекция, которую можно объявить и использовать с любым типом, например List<int>
List<string>
, или List<Person>
.
Чтобы понять, почему универсальные шаблоны полезны, давайте рассмотрим конкретный класс до и после добавления универсальных шаблонов: ArrayList В .NET Framework 1.0 элементы ArrayList
были типа Object. Любой элемент, добавленный в коллекцию, автоматически преобразуется в элемент Object
. То же самое произойдет при чтении элементов из списка. Этот процесс называется упаковкой и распаковкой, что влияет на производительность. Кроме вопросов производительности, однако, нет способа определить тип данных в списке во время компиляции, что делает код уязвимым. Универсальные шаблоны решают эту проблему, определив тип данных каждого экземпляра списка. Например, можно добавлять только целые числа в List<int>
и только людей в List<Person>
.
Универсальные шаблоны также доступны во время выполнения. Среда выполнения знает, какой тип структуры данных вы используете, и может хранить ее в памяти более эффективно.
В следующем примере показана небольшая программа, которая иллюстрирует эффективность знания типа структуры данных во время выполнения:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
namespace GenericsExample {
class Program {
static void Main(string[] args) {
//generic list
List<int> ListGeneric = new List<int> { 5, 9, 1, 4 };
//non-generic list
ArrayList ListNonGeneric = new ArrayList { 5, 9, 1, 4 };
// timer for generic list sort
Stopwatch s = Stopwatch.StartNew();
ListGeneric.Sort();
s.Stop();
Console.WriteLine($"Generic Sort: {ListGeneric} \n Time taken: {s.Elapsed.TotalMilliseconds}ms");
//timer for non-generic list sort
Stopwatch s2 = Stopwatch.StartNew();
ListNonGeneric.Sort();
s2.Stop();
Console.WriteLine($"Non-Generic Sort: {ListNonGeneric} \n Time taken: {s2.Elapsed.TotalMilliseconds}ms");
Console.ReadLine();
}
}
}
Эта программа создает выходные данные, аналогичные следующему:
Generic Sort: System.Collections.Generic.List`1[System.Int32]
Time taken: 0.0034ms
Non-Generic Sort: System.Collections.ArrayList
Time taken: 0.2592ms
Первое, что вы можете заметить здесь, заключается в том, что сортировка универсального списка значительно быстрее, чем сортировка не универсального списка. Вы также можете заметить, что тип универсального списка отличается ([System.Int32]), в то время как тип для не универсального списка обобщен. Так как среда выполнения знает, что универсальный List<int>
имеет тип Int32, она может хранить элементы списка в базовом массиве целых чисел в памяти, а неуниверсальный ArrayList
должен привести каждый элемент списка к объекту. Как показано в этом примере, дополнительные приведения данных занимают время и замедляют сортировку списка.
Дополнительное преимущество среды выполнения, знающей тип вашего обобщенного типа, — это улучшенный процесс отладки. При отладке универсального элемента в C#вы знаете, какой тип каждого элемента находится в структуре данных. Без универсальных шаблонов не было бы понятия о типе каждого элемента.