Ответ
Обобщения (Generics) — это механизм в C#, позволяющий создавать типобезопасные классы, структуры, интерфейсы и методы, которые работают с данными произвольного типа, указанного во время использования, а не во время объявления. Это устраняет необходимость приведения типов (casting) и проверок во время выполнения, перенося эти проверки на этап компиляции.
Основная идея: Параметризация типов. Вы пишете алгоритм или структуру данных один раз, но указываете конкретный тип, с которым будете работать, позже.
Базовый пример: обобщенный класс Repository<T>
// Объявление обобщенного класса с параметром типа T
public class Repository<T> where T : class, IEntity, new() // Ограничения (constraints)
{
private readonly List<T> _items = new();
public void Add(T entity)
{
_items.Add(entity);
}
public T GetById(int id)
{
// Компилятор знает, что T имеет свойство Id (благодаря ограничению IEntity)
return _items.FirstOrDefault(e => e.Id == id);
}
public T CreateNew()
{
return new T(); // Возможно благодаря ограничению 'new()'
}
}
// Использование с конкретными типами
var userRepo = new Repository<User>(); // T = User
userRepo.Add(new User { Id = 1, Name = "Alice" });
User foundUser = userRepo.GetById(1); // Без приведения типа! Тип возврата - User.
var productRepo = new Repository<Product>(); // T = Product
Product newProduct = productRepo.CreateNew();
Обобщенные методы:
// Метод, работающий с массивом любого типа
public static T FindMax<T>(T[] array) where T : IComparable<T>
{
if (array == null || array.Length == 0)
throw new ArgumentException("Array is empty or null");
T max = array[0];
for (int i = 1; i < array.Length; i++)
{
// Сравнение возможно благодаря IComparable<T>
if (array[i].CompareTo(max) > 0)
max = array[i];
}
return max; // Тип возврата - T
}
// Вызов
int maxInt = FindMax(new[] { 1, 5, 3, 9, 2 }); // T выводится как int
string maxString = FindMax(new[] { "apple", "zebra", "banana" }); // T = string
Ключевые преимущества:
-
Безопасность типов (Type Safety): Компилятор проверяет соответствие типов. Исключаются ошибки
InvalidCastExceptionво время выполнения.// Без Generics (старый ArrayList) - ОПАСНО ArrayList list = new ArrayList(); list.Add(42); // boxed to object list.Add("oops"); // тоже object int num = (int)list[1]; // Runtime InvalidCastException! // С Generics (List<T>) - БЕЗОПАСНО List<int> safeList = new List<int>(); safeList.Add(42); // safeList.Add("oops"); // ОШИБКА КОМПИЛЯЦИИ: нельзя добавить string в List<int> int safeNum = safeList[0]; // Приведение не требуется -
Производительность:
- Для значимых типов (value types) исключаются операции упаковки (boxing) и распаковки (unboxing), которые требуют выделения памяти в куче и снижают производительность.
- Код для конкретных типов генерируется во время JIT-компиляции, что приводит к высокооптимизированному машинному коду.
-
Повторное использование кода и читаемость: Один обобщенный алгоритм (например, сортировка, поиск) или структура данных (список, словарь) может использоваться с любым типом. Код становится чище и понятнее, так как исчезают приведения типов и
object.
Ограничения (Constraints): Позволяют указать требования к параметру типа T.
public class Processor<T> where T :
class, // T должен быть ссылочным типом
IDisposable, // T должен реализовывать интерфейс IDisposable
new() // T должен иметь публичный конструктор без параметров
{
public void Process()
{
T item = new T(); // Можно создать экземпляр
using (item) // Можно использовать в using, т.к. IDisposable
{
// ... работа с item
}
}
}
Важные встроенные обобщенные типы в .NET: List<T>, Dictionary<TKey, TValue>, Nullable<T>, Task<TResult>, все интерфейсы коллекций (IEnumerable<T>, ICollection<T>).
Видео-ответы
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶