Back to Blog
C#

Using the C# params Keyword for Variable Arguments

c# params keyword: Understand the C# params keyword: syntax, compiler behavior, overload resolution, allocation costs, and when to choose params over an explicit array.

C#params keywordvariable argumentsmethod overloadingarray parametersC# syntax
Illustration of a C# method accepting a variable number of arguments through the params keyword, shown as multiple values flowing into a single array parameter.

The c# params keyword lets a method accept a variable number of arguments of the same type. Instead of declaring several overloads for different argument counts, you declare one parameter that behaves like an array, and callers can pass zero, one, or many values directly.

What the params Keyword Does

A params parameter is a single-dimensional array parameter that the compiler treats specially. When a caller passes individual arguments, the compiler collects them into an array before the method body runs. The method itself sees only the array and has no way to tell whether the caller passed separate values or an existing array.

public static int Sum(params int[] numbers) { int total = 0; foreach (int n in numbers) { total += n; } return total; }

This method can be called in three equivalent ways:

int a = Sum(1, 2, 3); int b = Sum(); int c = Sum(new int[] { 1, 2, 3 });

The first call compiles to the same IL as the third: the compiler generates the array, fills it with the three values, and passes it to the method. The second call passes an empty array, not null.

Declaring a params Method

A params parameter must be a single-dimensional array, and it must be the last parameter in the signature. You cannot combine params with ref or out on the same parameter, and you cannot have more than one params parameter in a method.

public static string Join(string separator, params string[] values) { return string.Join(separator, values); }

The array type can be any type, including nullable reference types, value types, or generic type parameters when the method itself is generic:

public static T FirstOrDefault<T>(params T[] items) { return items.Length > 0 ? items[0] : default; }

How the Compiler Handles params Arguments

The compiler decides at the call site whether an array allocation is needed. If the caller passes individual arguments, the compiler emits code that creates a new array and stores each argument in it. If the caller passes an array directly, no new array is created and the same reference is passed to the method.

This distinction matters for two reasons. First, a method that mutates the array will affect the caller's array when the caller passed it directly. Second, the allocation cost appears at the call site, not inside the method, so a params method called thousands of times per second with individual arguments produces one array allocation per call.

Overload Resolution and params

When the compiler chooses between overloads, a method whose signature matches without expanding a params parameter is preferred over one that requires params expansion. This follows the normal overload resolution rules in the C# specification: a candidate that uses the normal form beats one that uses the expanded form.

public static void Print(string message) { Console.WriteLine(message); } public static void Print(params string[] messages) { foreach (var m in messages) { Console.WriteLine(m); } }

A call to Print("hello") binds to the single-parameter overload, not the params version. This is useful when you want to keep a common single-argument path free of array allocation while still supporting variable argument counts.

Common Usage Patterns

The most common use of params is in helper methods that format, combine, or aggregate a variable number of values. string.Format, Console.WriteLine, and Path.Combine are well-known examples in the .NET base class library.

A practical pattern is a validation helper that accepts multiple conditions:

public static void RequireAll(bool condition, params string[] messages) { if (!condition) { throw new ArgumentException(string.Join("; ", messages)); } }

Another pattern is building a composite from a variable number of parts:

public static Composite BuildComposite(params IComponent[] components) { return new Composite(components); }

When the number of arguments is small and fixed in practice, a params parameter keeps the call site readable without forcing callers to construct arrays manually.

Performance and Allocation Behavior

The main runtime cost of params is the array allocation when callers pass individual arguments. In a hot path, that allocation contributes to garbage collection pressure. The cost is usually small, but it is not zero.

If the same array is reused across calls, the allocation disappears entirely:

int[] values = { 10, 20, 30 }; for (int i = 0; i < 1000; i++) { Sum(values); // no new array per call }

When individual arguments are passed in a loop, each iteration allocates:

for (int i = 0; i < 1000; i++) { Sum(1, 2, 3); // new array per iteration }

The expanded form is also not eligible for the same compiler optimizations as a direct array parameter, because the array is constructed at the call site. For extremely hot paths, an explicit array parameter or separate overloads for common argument counts may be preferable.

Edge Cases and Limitations

Passing null as the array argument is legal:

Sum(null);

Inside the method, numbers will be null, so code that iterates without a null check throws NullReferenceException. The empty call Sum() produces an empty array, not null, so those two cases behave differently.

A params parameter cannot be combined with ref or out. If you need to modify the caller's array reference, you must pass the array explicitly and use ref on a separate parameter.

The params modifier is part of the method signature for overload resolution, but it does not affect the method's metadata in a way that changes the parameter type. The parameter is still an array; params is a calling convention applied by the compiler.

Choosing Between params and Explicit Array Parameters

Use params when callers benefit from passing values directly and the argument count is genuinely variable. Use an explicit array parameter when callers already have an array, when you want to require at least one element, or when the method is on a hot path where per-call allocation matters.

A method that requires at least one argument cannot express that requirement with params alone, because params allows zero arguments. In that case, declare a required first parameter and use params for the remainder:

public static int Sum(int first, params int[] rest) { int total = first; foreach (int n in rest) { total += n; } return total; }

This guarantees at least one value at compile time and keeps the variable-count convenience.

c# params keyword: Practical Usage and Code Examples | RYUSLOG DEV