Your programs are getting longer. Before you build the notebook in the next step, you’ll learn the most important tool for keeping things in order: a method of your own. A method is a piece of code with a name. You write it once and call it as often as you like. Console.WriteLine is a method too, just one that somebody else has already written.

Your first method

C#
// divider.cs
DrawDivider();
Console.WriteLine("Chapter 1");
DrawDivider();
Console.WriteLine("Once upon a time ...");

static void DrawDivider()
{
    Console.WriteLine("--------------------");
}
--------------------
Chapter 1
--------------------
Once upon a time ...

The method is at the bottom; at the top, it’s called twice. The parts of the first line, static void DrawDivider():

  • static means, here: the method works only with what it’s explicitly given. It doesn’t see the variables of the rest of the program. That keeps it independent and easy to understand.
  • void means: the method gives nothing back. It just does something.
  • DrawDivider is its name. Method names start with a capital letter and describe what the method does.
  • The empty parentheses mean: it gets nothing passed in.

In this course, the methods always go below the main program. They’re found all the same when their name comes up at the top: the compiler reads the whole file before the program starts.

If you want a longer divider, you change one single place, not every line where it appears. That’s the first big advantage of methods.

Parameters: giving a method something to work with

The parentheses hold the parameters: variables that the caller fills.

C#
// greet.cs
Greet("Ada");
Greet("Linus");
Underline("Weekly plan", '=');

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

static void Underline(string text, char symbol)
{
    Console.WriteLine(text);
    Console.WriteLine(new string(symbol, text.Length));
}
Hello, Ada!
Hello, Linus!
Weekly plan
===========

Underline has two parameters, separated by a comma. The second has the type char, a single character, written in single quotes: '='. new string(symbol, text.Length) builds a text out of as many of these characters as the heading is long. By the way, this also lets you draw the frame from lesson 3 so that it fits any name.

return: giving back a result

A method can calculate something and give the result back. Instead of void, the type of the result then goes before the name, and return gives the value back:

C#
// square.cs
int result = Square(7);
Console.WriteLine(result);
Console.WriteLine(Square(12));
Console.WriteLine(Square(Square(2)));

static int Square(int number)
{
    return number * number;
}
49
144
16

The call Square(7) stands where you could also write a number, and it turns into 49. That’s why one call can even sit inside another: Square(Square(2)) first calculates 2 times 2, then 4 times 4. return ends the method right away, even in the middle of a loop.

A method that saves you work

Reading numbers safely takes several lines every time. As a method, you write it once:

C#
// read-number.cs
int age = ReadNumber("How old are you? ");
int height = ReadNumber("How tall are you in inches? ");
Console.WriteLine($"{age} years, {height} inches.");

static int ReadNumber(string question)
{
    while (true)
    {
        Console.Write(question);
        string input = Console.ReadLine() ?? "0";
        if (int.TryParse(input, out int number))
        {
            return number;
        }
        Console.WriteLine("Please enter a whole number in digits.");
    }
}
How old are you? thirty-six
Please enter a whole number in digits.
How old are you? 36
How tall are you in inches? 68
36 years, 68 inches.

The method keeps asking until a number comes, and then gives it back with return. What’s new is ?? "0" instead of ?? "": if no input can come at all anymore (Ctrl+Z or Ctrl+D), the method takes 0 instead of asking forever. The main program at the top now reads almost like plain text: read a number, read another one, write both.

Good names

With methods, you decide how your program reads. A few rules that have stood the test of time:

  • Methods do something, so their name starts with a verb: ReadNumber, ShowAll, CalculateSum.
  • Variables say what’s in them: matchCount instead of x, searchTerm instead of s.
  • A method has one job. If the only way to name it uses “and,” such as ReadAndCheckAndSave, it’s probably three methods.

The last rule sounds small, but it’s the basis of everything professionals say about structuring software well. You’ll meet it again in the follow-up course under the name SOLID.

Exercise 1

Write the search for the largest number from lesson 7 as a method static int Largest(List<int> numbers). Call it in largest-method.cs for two different lists and print the results. You can assume that the lists aren’t empty.

Solution 1

C#
// largest-method.cs
List<int> temperatures = [54, 63, 48, 70, 59];
List<int> scores = [88, 95, 71];
Console.WriteLine($"Warmest day: {Largest(temperatures)} degrees");
Console.WriteLine($"Best score: {Largest(scores)}");

static int Largest(List<int> numbers)
{
    int largest = numbers[0];
    foreach (int number in numbers)
    {
        if (number > largest)
        {
            largest = number;
        }
    }
    return largest;
}
Warmest day: 70 degrees
Best score: 95

Once it’s written, the method works for any list. You know from lesson 7 what it would do with an empty list: numbers[0] wouldn’t exist, and the program would stop. That’s why the assumption is part of the exercise. In the follow-up course, you’ll learn how a method can report something like that properly by itself.

Exercise 2

Turn the leap year rule from lesson 5 into a method static bool IsLeapYear(int year). Use it in leap-years.cs to print every leap year from 1890 to 1920 on one line, separated by spaces.

Solution 2

C#
// leap-years.cs
for (int year = 1890; year <= 1920; year++)
{
    if (IsLeapYear(year))
    {
        Console.Write($"{year} ");
    }
}
Console.WriteLine();

static bool IsLeapYear(int year)
{
    return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
}
1892 1896 1904 1908 1912 1916 1920

There are eight years between 1896 and 1904: 1900 can be divided by 100 but not by 400, so it isn’t a leap year. The method gives back the result of the condition directly; it doesn’t need an if for that.

Exercise 3

Something repeats in this program. Tidy it up by writing a method ShowSection with two parameters. The output should stay the same.

C#
// tidy-up.cs
Console.WriteLine();
Console.WriteLine("== Shopping ==");
Console.WriteLine("Bread, milk, apples");
Console.WriteLine();
Console.WriteLine("== To do ==");
Console.WriteLine("Fix the bike");
Console.WriteLine();
Console.WriteLine("== Calls ==");
Console.WriteLine("Grandma");

Solution 3

C#
// tidy-up.cs
ShowSection("Shopping", "Bread, milk, apples");
ShowSection("To do", "Fix the bike");
ShowSection("Calls", "Grandma");

static void ShowSection(string title, string content)
{
    Console.WriteLine();
    Console.WriteLine($"== {title} ==");
    Console.WriteLine(content);
}

== Shopping ==
Bread, milk, apples

== To do ==
Fix the bike

== Calls ==
Grandma

Nine lines become three calls, and each one says what it does. If the heading should look different later, say with -- instead of ==, you change it in one single place. After a rework like this, always check that the output really stayed the same. Tidying up means: the code gets better, the behavior stays the same.

What you can do now

  • You write methods with static void Name() and call them.
  • You pass parameters to methods, several at once too.
  • You write methods that give back a result with return, and you use the call like a value.
  • You move repeated code into a method and then check that the behavior stays the same.
  • You choose names that say what a method does and what a variable holds.

Read more