C# 13: конец выделениям `params`
C# 13 наконец устраняет скрытое выделение массива за params. Теперь params можно использовать со Span, ReadOnlySpan, List и другими типами коллекций для вариадических методов без выделений.
Более двух десятилетий ключевое слово params в C# несло с собой скрытый налог — неявные выделения массивов. Каждый раз, когда вы вызывали метод вроде string.Format или свой собственный хелпер с переменным числом аргументов, компилятор молча создавал новый массив. В сценариях с высокой производительностью (горячих участках) эти выделения накапливались и создавали излишнее давление на сборку мусора (GC).
В C# 13 и .NET 9 этот налог наконец отменяется. Теперь вы можете использовать params с типами коллекций, отличными от массивов, включая Span<T> и ReadOnlySpan<T>.
Налог на массив
Рассмотрим типичный метод логирования до C# 13.
// Old C# way
public void Log(string message, params object[] args)
{
// ... logic
}
// Usage
Log("User {0} logged in", userId); // Allocates new object[] { userId }
Даже если вы передавали единственное целое число, среде выполнения приходилось выделять массив в куче. Для библиотек вроде Serilog или логирования ASP.NET Core это означало изобретать обходные пути или перегружать методы с 1, 2, 3… аргументами, чтобы избежать массива.
Ноль выделений с params ReadOnlySpan<T>
C# 13 позволяет применять модификатор params к любому типу, поддерживающему выражения коллекций. Самое заметное изменение — поддержка ReadOnlySpan<T>.
// C# 13 way
public void Log(string message, params ReadOnlySpan<object> args)
{
// ... logic using span
}
// Usage
// Compiler uses stack allocation or shared buffers!
Log("User {0} logged in", userId);
Когда вы вызываете этот новый метод, компилятор достаточно умён, чтобы передать аргументы через буфер, выделенный на стеке (через stackalloc), или применить другие оптимизации, полностью обходя кучу.
За пределами массивов
Дело не только в производительности. params теперь поддерживает List<T>, HashSet<T> и IEnumerable<T>. Это повышает гибкость API, позволяя выразить намерение относительно структуры данных, а не навязывать массив.
public void ProcessTags(params HashSet<string> tags)
{
// O(1) lookups immediately available
}
ProcessTags("admin", "editor", "viewer");
Когда переходить
Если вы поддерживаете библиотеку или приложение, чувствительное к производительности, на .NET 9, пересмотрите свои методы с params.
- Замените
params T[]наparams ReadOnlySpan<T>, если вам нужно только читать данные. - Переходите на
params IEnumerable<T>, если требуется отложенное выполнение или универсальная гибкость.
Это небольшое изменение сигнатуры может существенно сократить трафик памяти за время жизни приложения.
Comments
Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.