Как реализованы дженерики?csharp-126

1. Основные концепции дженериков

Дженерики (обобщенные типы) - это механизм, позволяющий создавать классы, интерфейсы и методы с заполнителями для типов, которые заменяются конкретными типами во время компиляции или выполнения.

public class List<T>  // T - параметр типа
{
    public void Add(T item) { ... }
    public T Get(int index) { ... }
}

// Использование
var intList = new List<int>();  // int - аргумент типа

2. Как работают дженерики на уровне CLR

2.1. Специализация типов во время выполнения

В отличие от шаблонов C++, .NET дженерики:

  • Реализованы на уровне CLR
  • Специализируются во время выполнения
  • Создают отдельные типы для значимых типов (value types)
// В runtime будут созданы разные специализации:
List<int>    // Отдельный тип
List<string> // Отдельный тип
List<object> // Отдельный тип

2.2. Общие реализации для ссылочных типов

Для ссылочных типов используется общая реализация (из-за одинакового размера указателей):

// Эти специализации используют одну реализацию в runtime:
List<string>
List<Stream>
List<MyClass>

3. Внутреннее представление дженериков

3.1. Метаданные в IL-коде

При компиляции в IL сохраняется информация:

  • Параметры типа
  • Ограничения (constraints)
  • Обобщенные методы
.class public auto ansi beforefieldinit List`1<T>
{
    .method public hidebysig instance void Add(!T item) cil managed
}

3.2. JIT-компиляция

При первом использовании:

  1. JIT-компилятор создает специализированный код
  2. Для значимых типов - отдельная реализация
  3. Для ссылочных типов - общая реализация

4. Ограничения дженериков

public T Max<T>(T a, T b) where T : IComparable<T>
{
    return a.CompareTo(b) > 0 ? a : b;
}

Основные виды ограничений:

  • where T : struct - значимый тип
  • where T : class - ссылочный тип
  • where T : new() - имеет конструктор по умолчанию
  • where T : BaseClass - наследование от класса
  • where T : ISomeInterface - реализация интерфейса

5. Ковариантность и контравариантность

5.1. Ковариантность

IEnumerable<out T>  // T только в выходных позициях
IEnumerable<string> strings = ...;
IEnumerable<object> objects = strings; // Безопасно

5.2. Контравариантность

IComparer<in T>     // T только во входных позициях
IComparer<object> objComparer = ...;
IComparer<string> strComparer = objComparer; // Безопасно

6. Особенности реализации

  1. Отражение (Reflection):

    • Можно исследовать параметры типа через typeof(T)
    • Type.GetGenericArguments() для анализа
  2. Статические поля:

    • Каждая специализация имеет свои статические поля
class Counter<T>
{
    public static int Count;
}

Counter<int>.Count++; // Не влияет на Counter<string>.Count
  1. Ограничения производительности:
    • Нет накладных расходов на boxing для значимых типов
    • Виртуализация вызовов для ссылочных типов

Резюмируем:

дженерики в .NET реализованы на уровне CLR с поддержкой runtime-специализации, что обеспечивает безопасность типов и производительность без дублирования кода. Механизм включает сложную систему ограничений, вариантности и интеграцию с системой типов .NET.