Обзор вопросов по языка програмиированию C# и технологии NET
Generics — это параметризованные типы и методы. Вместо конкретного типа вы пишете параметр типа T (или несколько), и этот код работает для любого типа, соответствующего ограничениям. Примеры: List<T>, Dictionary<TKey, TValue>, void Swap<T>(ref T a, ref T b).
Зачем: типобезопасность + отказ от бокcинга для value-types + переиспользуемость кода.
// generic класс
public class Box<T>
{
public T Value { get; set; }
}
// generic метод
public static void Swap<T>(ref T a, ref T b) { var t = a; a = b; b = t; }
// generic интерфейс
public interface IRepository<T> { void Add(T item); T Get(int id); }
При вызове компилятор обычно выводит T автоматически:
var b = new Box<int> { Value = 5 }; // или просто new Box<int>()
Swap<int>(ref x, ref y); // тип можно явно указать
typeof(List<int>) и typeof(List<string>) — разные типы в отражении.Практическое следствие:
typeof(List<>) → MakeGenericType(typeof(int)).default(T), typeof(T), T[] и т.д. на уровне IL/runtime.List<int> и List<double> — разные скомпилированные версии). Это даёт отсутствие бокcинга и высокую производительность.Следствие:
List<int> хранит int без boxing).StaticHolder<int>.X и StaticHolder<string>.X — разные поля.where T : ... — позволяет накладывать ограничения на типы-параметры.
Частые варианты:
where T : class — ссылка (reference type).where T : struct — non-nullable value type (заметь: int? не допускается).where T : new() — должен иметь публичный конструктор без параметров.where T : SomeBaseClass — наследник указанного класса.where T : ISomeInterface — реализует интерфейс.where T : unmanaged — blittable unmanaged type (нет управляемых ссылок).where T : notnull — запрещает null (включая nullable reference types).where T : System.Enum / where T : System.Delegate — специальные ограничения для enum/delegate.Пример:
public T CreateInstance<T>() where T : class, new()
{
return new T();
}
Частые вопросы:
new T() без new()? — Компилятор не гарантирует, что T имеет публичный параметр less ctor.where T : struct — запрещает Nullabledefault(T) даёт null для ссылочных типов, нулевое значение (0, false, '\0') для value-types.default сокращённо в коде (контекстно).Объявления out и in:
out T — ковариантность (можно присваивать IEnumerable<string> в IEnumerable<object>).in T — контрвариантность (для делегатов/интерфейсов-«потребителей», например IComparer<in T>).out/in к классам.Пример:
IEnumerable<string> ss = new List<string>();
IEnumerable<object> oo = ss; // OK — IEnumerable<out T> ковариантен
Проверки безопасности:
out T нельзя принимать T в качестве параметра метода — только возвращать/выдавать.in T нельзя возвращать T — только принимать.public void Do<T>(T item) { ... }
Do<int>(5).typeof(List<>) — открытый generic type definition.typeof(List<int>) — constructed (closed) generic type.Type.IsGenericType, Type.IsGenericTypeDefinition, GetGenericArguments(), GetGenericTypeDefinition(), MakeGenericType(...).MethodInfo.IsGenericMethodDefinition, MakeGenericMethod(...).Пример:
var open = typeof(Dictionary<,>);
var closed = open.MakeGenericType(typeof(string), typeof(int));
constrained. — помогает вызывать интерфейсные/виртуальные методы на value-types без бокcинга (runtime знает, как это делать).List<T> vs Array — array of T удобен и быстрый; generic коллекции лучше для абстракций.new T() — требует new() constraint; если нужен non-public ctor — нельзя.List<string> в List<object> (это нельзя).string[] → object[] разрешено, но при попытке записать object в string[] будет ArrayTypeMismatchException.where T : struct ≠ where T : new() — разные семантики.T — value type и вы приводите List<T> к IList/IEnumerable без generic, могут быть бокcинги/обёртки.Nullable<T> и where T : struct — Nullable<T> НЕ проходит where T : struct (как правило), потому интервьюер может спросить это как ловушку.(коротко — для использования в интервью)
new T()? — Только с where T : new() constraint.out/in — позволяет безопасно преобразовать generic интерфейсы/делегаты между совместимыми reference-типами.== для T? — Нет гарантии: == действует для тех типов, где он определён; лучше использовать EqualityComparer<T>.Default.Equals(a,b).Generic repository
public interface IRepository<T> where T : IEntity
{
void Add(T entity);
T? Get(int id);
}
Generic singleton (статик per-T)
public static class SingletonHolder<T> where T : new()
{
public static readonly T Instance = new T();
}
Covariant interface
public interface IReadOnlyList<out T>
{
T Get(int index); // только возвращаем T — разрешено
}
Generic method with interface constraint
public T Max<T>(T a, T b) where T : IComparable<T>
=> a.CompareTo(b) >= 0 ? a : b;
(сформулируй на собесе коротко — и можешь раскрыть, если попросят)
«Generics в .NET — стираются или реальные?» ⇒ Реальные (reified). Метаданные сохраняют параметры типов.
**«Почему List
**«Можно ли присвоить List
**«Сколько статических полей у GenericClass
«Можно ли использовать nullable типы с where T : struct?»
⇒ Нет — where T : struct запрещает nullable value types (ожидается non-nullable value type).
«Что такое constrained. в IL?» ⇒ Это механизм CLR, позволяющий вызывать виртуальные/интерфейсные методы на value-type без бокcинга.
«Почему иногда JIT шарит код для reference types?» ⇒ Для экономии нативного кода: одна версия кода способна обслуживать разные ссылочные типы.
«Можно ли new T[size] внутри generic метода?»
⇒ Да, new T[n] разрешён (создаётся массив элементов типа T).
«Equality for T — как правильно?»
⇒ Используйте EqualityComparer<T>.Default — он корректно обрабатывает value/reference types и пользовательские overrides.
«Почему generic constraints влияют на производительность?» ⇒ Потому что наличие конкретных интерфейсных/методных ограничений позволяет компилятору/CLR генерировать эффективные вызовы (и избежать рефлексии/boxing).
Func<out T> — ковариантен в возвращаемом типе; Action<in T> — контрвариантен в параметре.TypeLoadException.new()/class/struct constraints, почему generics убирают boxing, List<T>/Dictionary<TKey,TValue>.constrained.), performance tradeoffs (code bloat vs speed), advanced constraints (unmanaged, notnull), generics + interop/unsafe, memory & JIT internals.✨Dvurechensky✨