Parameters and Arguments
Parameters are the inputs a method declares; arguments are the values you pass when you call it. C# gives you flexible ways to pass them: by position or by name, with default values, or as a variable-length list. This lesson also explains what happens to an argument when the method changes it.
Several parameters
Arguments are matched to parameters by position, and their types must fit:
Console.WriteLine(Bmi(70, 1.75)); static double Bmi(double weightKg, double heightM) { return Math.Round(weightKg / (heightM * heightM), 1); }
22.9
Console.WriteLine(Bmi("70", 1.75)); static double Bmi(double weightKg, double heightM) => weightKg / (heightM * heightM);
Program.cs(1,23): error CS1503: Argument 1: cannot convert from 'string' to 'double'
Named arguments
You can name arguments at the call site. This makes calls with several similar values self-explanatory, and lets you pass them in any order:
Book("Lisbon", nights: 3, guests: 2); Book(guests: 4, city: "Kyoto", nights: 5); static void Book(string city, int nights, int guests) { Console.WriteLine($"{city}: {nights} nights for {guests} guests"); }
Lisbon: 3 nights for 2 guests Kyoto: 5 nights for 4 guests
Compare Book("Lisbon", 3, 2) — which number is which? Named arguments are especially useful for bool parameters: Save(file, overwrite: true) reads far better than Save(file, true).
Optional parameters
Give a parameter a default value and callers can leave it out. Optional parameters must come after the required ones:
Console.WriteLine(Greet("Kai")); Console.WriteLine(Greet("Kai", "Good evening")); Console.WriteLine(Greet("Kai", punctuation: "?")); static string Greet(string name, string greeting = "Hello", string punctuation = "!") { return $"{greeting}, {name}{punctuation}"; }
Hello, Kai! Good evening, Kai! Hello, Kai?
Combine optional and named parameters to skip over defaults you don't want to change, as in the third call. Default values must be compile-time constants (numbers, strings, null, default).
params: any number of arguments
A params parameter collects any number of arguments into a collection. It must be the last parameter:
Console.WriteLine(Sum()); Console.WriteLine(Sum(5)); Console.WriteLine(Sum(1, 2, 3, 4)); int[] existing = [10, 20, 30]; Console.WriteLine(Sum(existing)); // an array can be passed directly static int Sum(params int[] numbers) { int total = 0; foreach (int n in numbers) total += n; return total; }
0 5 10 60
Since C# 13, params works with other collection types too — such as params ReadOnlySpan<int>, which avoids allocating an array for each call — and you can combine it with regular parameters:
Log("INFO", "Server started"); Log("WARN", "Disk", "at", "91%"); static void Log(string level, params ReadOnlySpan<string> words) { Console.WriteLine($"[{level}] {string.Join(' ', words)}"); }
[INFO] Server started [WARN] Disk at 91%
Console.WriteLine and string.Join themselves use params.
What happens to arguments: value types
By default, C# passes a copy of the argument. For value types like int, changing the parameter inside the method has no effect on the caller's variable:
int score = 10; AddBonus(score); Console.WriteLine(score); static void AddBonus(int s) { s += 5; Console.WriteLine($"Inside: {s}"); }
Inside: 15 10
The method should return the new value instead: score = AddBonus(score);. (Lesson 22 shows ref, which lets a method change the caller's variable directly.)
What happens to arguments: reference types
For reference types — arrays, lists, objects — the copy is a copy of the reference. Both the caller and the method point at the same object, so changes to its contents are visible to the caller. Replacing the whole object is not:
int[] prices = [10, 20, 30]; DoubleAll(prices); Console.WriteLine(string.Join(", ", prices)); Replace(prices); Console.WriteLine(string.Join(", ", prices)); static void DoubleAll(int[] values) { for (int i = 0; i < values.Length; i++) values[i] *= 2; // changes the shared array } static void Replace(int[] values) { values = [0, 0, 0]; // only changes the local copy of the reference }
20, 40, 60 20, 40, 60
Methods that change their arguments can surprise callers. Prefer returning new values, and make it obvious in the name (DoubleAll, SortInPlace) when a method does modify what it's given.
Returning several values with tuples
A method can return a tuple — several values grouped together — and the caller can unpack it into separate variables:
int[] data = [4, 9, 1, 7, 3]; var (min, max, avg) = Stats(data); Console.WriteLine($"min {min}, max {max}, average {avg}"); var result = Stats([2, 4]); Console.WriteLine(result.Max); static (int Min, int Max, double Average) Stats(int[] values) { return (values.Min(), values.Max(), values.Average()); }
min 1, max 9, average 4.8 4
Try it yourself
Add an optional decimals parameter and call the method with named arguments.
1.61 km 42.2 km 3.1 mi