Methods

C# · Lesson 14 of 31 6 min read Updated

As programs grow, you will notice the same few lines appearing again and again: calculate GST, check a PIN code, print a bill line. A method gives a block of code a name so you can run it whenever you need it, with different inputs each time. Methods make code shorter, easier to test and much easier to read.

In this lesson you will write methods that take parameters and return values, learn the difference between local functions and class methods, and use overloading, optional arguments, ref and out, params, tuples and recursion.

Your first methods

A method has a return type, a name, a list of parameters in brackets, and a body. void means "returns nothing". In a file with top-level statements, you can write methods at the bottom of the file; they are called local functions.

C#
Greet("Isha");
Greet("Rohan");

int area = Square(12);
Console.WriteLine($"Area: {area} sq ft");
Console.WriteLine(IsValidPin("124001"));
Console.WriteLine(IsValidPin("12400"));

void Greet(string name)
{
    Console.WriteLine($"Good morning, {name}!");
}

int Square(int side) => side * side;

bool IsValidPin(string pin)
{
    if (pin.Length != 6)
    {
        return false;
    }
    return pin.All(char.IsDigit);
}
Output
Good morning, Isha!
Good morning, Rohan!
Area: 144 sq ft
True
False
  • name and side are parameters: variables that receive the values you pass. "Isha" and 12 are the arguments.
  • return sends a value back and ends the method immediately.
  • int Square(int side) => side * side; is an expression-bodied method: a one-line method written with =>.
  • A method name says what it does, in PascalCase: IsValidPin, CalculateFare. Methods returning bool often start with Is, Has or Can.

Methods in a class: overloading, optional and named arguments

Most methods in real programs live inside classes. A static method belongs to the class itself, so you call it as ClassName.Method(...). Inside a class, several methods can share a name if their parameter lists differ. This is called overloading.

C#
Console.WriteLine(Billing.Total(499));
Console.WriteLine(Billing.Total(499, 2));
Console.WriteLine(Billing.Total(1299, 1, delivery: 0));
Console.WriteLine(Billing.Total(1299.50m, 2));

static class Billing
{
    public static int Total(int price, int qty = 1, int delivery = 40)
        => price * qty + delivery;

    public static decimal Total(decimal price, int qty)
        => price * qty + 40;
}
Output
539
1038
1299
2639.00
  • qty = 1 and delivery = 40 are optional parameters with default values; callers may leave them out.
  • delivery: 0 is a named argument. It makes the call readable and lets you skip optional parameters in the middle.
  • The compiler picks the overload that best matches the argument types: 1299.50m is a decimal, so the second Total runs.

Local functions cannot be overloaded; overloading is a feature of class methods.

Passing values in and getting several values out

By default, arguments are passed by value: the method gets a copy of the variable's value. For value types such as int, changing the parameter does not affect the caller. The ref keyword passes the variable itself, and out lets a method hand back an extra result. Often, a tuple is the cleanest way to return several values.

C#
int marks = 38;
AddGrace(ref marks, 5);
Console.WriteLine($"After grace: {marks}");

bool ok = TryDivide(10, 0, out int result);
Console.WriteLine($"Division worked: {ok}, result: {result}");

(int min, int max, double avg) = Stats([66, 91, 78, 54]);
Console.WriteLine($"Min {min}, max {max}, average {avg}");

void AddGrace(ref int value, int grace)
{
    value += grace;
}

bool TryDivide(int a, int b, out int quotient)
{
    if (b == 0)
    {
        quotient = 0;
        return false;
    }
    quotient = a / b;
    return true;
}

(int Min, int Max, double Average) Stats(int[] values)
{
    return (values.Min(), values.Max(), values.Average());
}
Output
After grace: 43
Division worked: False, result: 0
Min 54, max 91, average 72.25
  • ref must be written both in the method and at the call: AddGrace(ref marks, 5). The method can read and change the caller's variable.
  • An out parameter must be assigned before the method returns. You have already used this pattern with int.TryParse.
  • The tuple return type (int Min, int Max, double Average) groups three values, and (int min, int max, double avg) = ... deconstructs them into three variables.

Use ref and out sparingly. A tuple or a small record (see Records, Structs & Enums) usually makes code clearer.

params: any number of arguments

A params parameter collects any number of arguments into one collection, which is how Console.WriteLine and string.Join accept many values.

C#
Console.WriteLine(SumAll(1, 2, 3));
Console.WriteLine(SumAll());
Console.WriteLine(SumAll(500, 250, 125, 125));

int SumAll(params int[] numbers)
{
    int total = 0;
    foreach (int n in numbers)
    {
        total += n;
    }
    return total;
}
Output
6
0
1000

Recursion

A recursive method calls itself. Every recursive method needs a base case that stops the calls, and each call must move closer to it.

C#
Console.WriteLine(Factorial(5));
Console.WriteLine(DigitSum(124001));

long Factorial(int n) => n <= 1 ? 1 : n * Factorial(n - 1);

int DigitSum(int n) => n < 10 ? n : n % 10 + DigitSum(n / 10);
Output
120
8

Factorial(5) becomes 5 * Factorial(4), then 5 * 4 * Factorial(3), and so on until Factorial(1) returns 1. DigitSum adds the last digit to the digit sum of the rest: 1 + 0 + 0 + 4 + 2 + 1 = 8. Without a base case, the calls never end and the program crashes with a stack overflow. For most everyday problems a loop is simpler; recursion shines with tree-shaped data such as folders inside folders.

Common mistakes

  • Not returning on every path. If one branch has no return, you get CS0161: not all code paths return a value.
  • Ignoring the return value. Square(5); on its own computes 25 and throws it away. Store or print it.
  • Expecting a change without ref. Assigning to an int parameter does not change the caller's variable.
  • Overloading local functions. Two local functions with the same name in one scope is an error; put overloads in a class.
  • Methods that do too much. If a method needs a long comment to explain it, split it into smaller methods with clear names.

Key points

  • A method has a return type, name, parameters and body; void returns nothing.
  • Top-level programs can have local functions; class methods are declared static or as instance methods.
  • Overloads share a name but differ in parameters; optional parameters and named arguments make calls flexible.
  • Arguments are passed by value; ref passes the variable itself and out returns extra values.
  • Tuples return several values neatly; params accepts any number of arguments.
  • Recursive methods need a base case.

Exercise

Write three methods for a Jaipur hotel. RoomCharge(int nights, int ratePerNight = 2500) returns the total room charge. Gst(int amount) returns 12% GST for amounts up to Rs 7,500 and 18% above that, as a decimal. Bill(string guest, int nights) prints a line with the guest's name, room charge, GST and total. Call Bill for Kabir (2 nights) and Meera (4 nights). (GST rates are simplified for practice.)

Show solution
C#
Bill("Kabir", 2);
Bill("Meera", 4);

int RoomCharge(int nights, int ratePerNight = 2500) => nights * ratePerNight;

decimal Gst(int amount)
{
    decimal rate = amount <= 7500 ? 0.12m : 0.18m;
    return amount * rate;
}

void Bill(string guest, int nights)
{
    int room = RoomCharge(nights);
    decimal gst = Gst(room);
    Console.WriteLine($"{guest}: room Rs {room}, GST Rs {gst}, total Rs {room + gst}");
}
Output
Kabir: room Rs 5000, GST Rs 600.00, total Rs 5600.00
Meera: room Rs 10000, GST Rs 1800.00, total Rs 11800.00

RoomCharge uses its default rate because Bill passes only nights. Multiplying an int by a decimal rate gives a decimal with two decimal places, which is why the GST prints as 600.00.

Quick Quiz

1. What does void mean in a method declaration?

2. Given int Add(int a, int b = 10) => a + b;, what is Add(5)?

3. Which keyword lets a method change the caller's int variable?

4. What does every recursive method need?

Learn C# with a mentor

Free lessons get you started. Our Programming & Software Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call