Methods in C#

Methods

The Crash Course

  • Methods are a way of taking a chunk of code that does a specific task, and putting it together in a way that it is reusable.
  • Stuff can be returned from a method by specifying the return type in the method definition, and then using the return keyword inside of the method anywhere you want to return a value, very similar to C, C++, and Java.
  • Parameters can be passed into a method, also in a way that is very similar to C, C++, and Java.
  • Multiple methods with the same name can be made (called method overloading) as long as their method signatures are different.
  • Recursion is where you call a method from inside itself, and requires a base case to prevent the flow of execution from digging deeper and deeper into more method calls, eventually killing itself in a StackOverflowException.

Introduction

Think about a program like Microsoft Word 2010, or a game like Gears of War. Think about how much code goes into creating something that big. They're enormous! Especially when compared to the little tiny programs that we've been working on so far.

How do you think you could manage all of that code? Where would you look if there was a bug?

As programmers, we've learned that when you have a large program, the best way to manage it is by breaking it down into smaller, more manageable pieces. This even gives us the ability to reuse these pieces. This basic concept is one we'll see over and over again, and it is called "divide and conquer".

C# provides us with a feature that allows us to break down our program into more manageable pieces. This feature is called a "method". Methods are sometimes called "functions" or "procedures" in other programming languages. You can take a piece of your code, place it inside of a method, and then access it whenever you want. We can more easily locate a bug or problem in a program, because we can pinpoint it to a very specific method. In addition, we can reuse a method as many times as we want, which means we don't have to reinvent the wheel, and potentially save ourselves tons of time.

In this tutorial, we'll take a look at how to make a method, and how to use a method (called "calling" a method), as well as how to get data back from a method and send data to a method. Next, we'll look at creating multiple methods with the same name, a process called "overloading". We'll then take another look at the Math class, which we've been using in previous tutorials, this time, with the new knowledge we have about methods. We'll wrap up our discussion about methods with a brief discussion on a powerful, but often confusing (for new people) trick that you can use with methods called "recursion".

Creating a Method

Let's get started by creating our first method. If you start with a brand new project, you'll see yourself staring down at code that looks like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;
using System.Net;
 
namespace MethodsTutorial
{
    public class Program
    {
        static void Main(string[] args)
        {
        }
    }
}

If you remember from the hello world tutorial, this template code already defines a method (the Main method) for us. At the time, we didn't know what a method was, but now we do. You can see from the template code, that the Main method is contained inside of a class called Program. (We're still getting to classes. Next tutorial.) When it comes down to it, we've already been using a method, without even really knowing it!

Basically, all methods belong to a class. (Well, pretty close.) When we go to create a new method, we're going to add it to a class as well. We'll add our method to the Program class, just like the Main method is.

Let's add in to our code the following:

static void CountToTen()
{
    for(int index = 1; index <= 10; index++)
    {
        Console.WriteLine(index);
    }
}

So now our full code should look like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;
using System.Net;
 
namespace MethodsTutorial
{
    public class Program
    {
        static void Main(string[] args)
        {
        }
 
        static void CountToTen()
        {
            for (int index = 1; index <= 10; index++)
            {
                Console.WriteLine(index);
            }
        }
    }
}

This code won't do anything yet. We still need to "call" the method, which we'll do in a second. But first, let me explain the piece of code that we've just added.

There are just a few simple pieces to what we just added. The first piece is the static keyword. We'll talk about this a little more in the next tutorial, when we look at classes in more detail. For the moment, just consider it a piece that we've always got to put in there. (It turns out, most of your methods won't actually need it, but for the way we're doing things here, it's what we've got to do to make it work.)

The second thing we added is the keyword void. We'll talk more about this in a second, when we discuss returning stuff from a method, but basically, the void keyword tells us our method isn't going to give us anything back. It's just going to do what it does, and be done.

The next part is the method's name: CountToTen. Like with variables, you can name your method all sorts of things. You usually want to be somewhat descriptive with your method names, to make it easier to explain what it does. Also, you should add a comment above your method to describe what it does. While it is not required, method names are usually have the first letter capitalized, while variables usually have a lowercase first letter.

Third, we have the parentheses. Again, we'll talk more about this later, this time, in the "Handing Stuff Off to a Method" section below. The stuff contained in the parentheses is called the method's arguments. For now, we are not using any arguments for this method, so there's nothing between the parentheses.

Fourth, we have a set of curly braces ('{' and '}'), where we put the body of the method—in other words, where we put the code that the method should do.

We then have a simple for loop, like we've seen before. Any of the code that we've written before can go inside of a method.

It is also worth mentioning that the order that you put your methods in, inside of a class, doesn't matter. In the example above, we could have put CountToTen above Main, and it wouldn't have made a difference. (For those of you with a C or C++ background, this is a major change. You no longer need to make function prototypes or anything like that.)

"Calling" a Method

Now that' we've got our method created, we want to be able to use it. Doing this is really easy. Inside of your Main method, you do the following:

CountToTen();

So your complete code would look like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;
using System.Net;
 
namespace MethodsTutorial
{
    public class Program
    {
        static void Main(string[] args)
        {
            CountToTen();
        }
 
        static void CountToTen()
        {
            for (int index = 1; index <= 10; index++)
            {
                Console.WriteLine(index);
            }
        }
    }
}

Now you can run your program (which automatically starts in the Main method) and see that it jumps over to the CountToTen method and does everything inside of it. When it reaches the end of a method, it goes back to where it came from. So looking at this code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;
using System.Net;
 
namespace MethodsTutorial
{
    public class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("I'm about to go into a method.");
 
            DoSomethingAwesome();
 
            Console.WriteLine("I'm back from the method.");
        }
 
        static void DoSomethingAwesome()
        {
            Console.WriteLine("I'm inside of a method, doing something awesome!");
        }
    }
}

This will result in the following output:

I'm about to go into a method.
I'm inside of a method, doing something awesome!
I'm back from the method.

It is also worth mentioning that you can call a method from inside of any other method, so you can go crazy creating and calling methods all over in your program. (In fact, that's what software engineering is! Relatively managed craziness, creating and calling methods.)

Returning Stuff from a Method

Methods are created to do something. Often, though, we want a method to actually do something, and give us the results back. A method has the option of doing this—giving something back. This is called "returning" something.

So far, all of the methods we've looked at haven't returned anything. If you'll look back, you'll see that we've been putting the void keyword before our method name, when we create a method. This keyword means the method doesn't return anything. We can change that, and have a method give us something back. To do this, instead of the void keyword, we simply place one of the variable types (like int, float, string, etc.) there instead. Then our method can return a value with that type.

Inside of a method, to return a value, we use the return keyword, followed by the value we want to return (followed by the standard semicolon). A very simple example of returning a value is this:

static float GetRandomNumber()
{
    return 4.385f;
}

You can see that we start off by stating the return type (float, in this case, meaning we're going to return something of the type float), and inside of the method, we simply use the return keyword, along with the value we want to return.

Let's go on to a more sophisticated example. Several times before, in these tutorials, we've done things like ask the user to type in a number. In this example, we'll create a method that asks the user for a number between 1 and 10, and keeps asking until they finally give us something that works. At that point, we'll return the number the user gave us. Here it is:

static int GetNumberFromUser()
{
    int usersNumber = 0;
 
    while(usersNumber < 1 || usersNumber > 10)
    {
        Console.Write("Enter a number between 1 and 10:  ");
        string usersResponse = Console.ReadLine();
        usersNumber = Convert.ToInt32(usersResponse);
    }
 
    return usersNumber;
}

If your method returns nothing (void), you don't need a return statement. But if it does return something, we're required to have a return statement.

If a method returns something, we can catch what the method returns in a variable of the matching type (or we can use explicit or implicit typecasting to convert it). By the way, when I say "catch", I'm using the term lightly. When we discuss exceptions in a few tutorials, we'll see how to use a specific catch keyword, which has an entirely different meaning. To do something with the results returned from a method, you simply do this:

int usersNumber = GetNumberFromUser();

Does that look familiar? It should. We've been doing stuff like that all along! Now we finally understand what's going on though. For instance, when we've written things like string usersResponse = Console.ReadLine();, we're simply calling a method (ReadLine) that belongs to the Console class, and getting the value that was returned by it!

It is worth mentioning here, that while the return statement is often the last line in a method, it doesn't have to be. For instance, you can have inside of a method, an if-statement that returns a value from a method only when a certain condition is met:

static int CalculatePlayerScore()
{
    int livesLeft = 3;
    int underlingsDestroyed = 17;
    int minionsDestroyed = 4;
    int bossesDestroyed = 1;
 
    // If the player is out of lives, they lose all of their points.
    if(livesLeft == 0)
    {
        return 0;
    }
 
    // Otherwise, the player gets 10 points for every underling destroyed,
    // 100 points for every minion destroyed, and 1000 points for every boss
    // destroyed.
    return underlingsDestroyed * 10 + 
            minionsDestroyed * 100 + 
            bossesDestroyed * 1000;
}

Also, if your method returns void, while you don't need the return keyword anywhere, you are allowed to put it wherever you want, all by itself (since you aren't returning anything) like this:

static void DoSomething()
{
    int aNumber = 1;
 
    if(aNumber == 2)
    {
        return;
    }
 
    Console.WriteLine("This only gets printed if the 'return' statement wasn't executed.");
}

Note that as soon as a return statement is hit, the flow of execution immediately goes back to where the method was called from—nothing more gets executed in the method.

Handing Stuff Off to a Method

Sometimes, we want a method to do stuff with certain pieces of information. We can hand stuff off to a method by putting what we want it to work with inside of the parentheses:

static void Count(int numberToCountTo)
{
    for(int current = 1; current <= numberToCountTo; current++)
    {
        Console.WriteLine(current);
    }
}

Where you define the method, you will put the type of the variable, and give it a name to use throughout the method. numberToCountTo, in this case, is what we call a "parameter".

When you call a method that has a parameter, you can "pass in" a value to the method, or hand off a value to a method to work with by putting it in the parentheses as well:

Count(5);
Count(15);

This code, in conjunction with the example before it, will first print out the numbers 1 through 5, then it will print out the numbers 1 through 15.

Passing in Multiple Parameters

You can also create a method that passes in multiple parameters. While the example below is overkill, it shows a simple example of passing two numbers into a Multiply method that multiplies them together, and then returns it:

static int Multiply(int a, int b)
{
    return a * b;
}

This example also shows that it is possible to both pass in parameters, and return a result in the same method. In fact, this is extremely common.

Method Overloading

One cool, but potentially confusing thing that you can do with methods is create multiple methods with the same name. This is called "method overloading", or simply "overloading".

The key is, while two methods can have the same name, they can't have the same "signature". A method's signature is defined as the combination of the method name and the types and order of the parameters that get passed in. Note that this does not include the parameters' names, just their types. (This is because when you call a method, the C# compiler can figure out which of the different "overloads" is supposed to be used based on the method name and the kinds of data that are being passed in, though the actual names of the variables don't matter when you're calling a method, so they are ignored.)

As an example, to help illustrate what a method signature is, take the following example:

static int Multiply(int a, int b)
{
    return a * b;
}

This has a signature that looks like this: static int Multiply(int, int).

You can only overload a method if you use a different signature. So for instance, the following code snippet works:

static int Multiply(int a, int b)
{
    return a * b;
}
 
static int Multiply(int a, int b, int c)
{
    return a * b * c;
}

This works, because the two multiply methods have a different number of parameters, and so as a result, a different signature. We could also define a Multiply method that has no parameters (just int Multiply()) or one parameter (int Multiply(int)) though, admittedly, in this particular case, I can't imagine how either of those two methods would do multiplication with zero or one things. (Hey, it's just an example!) Likewise, you could define a Multiply method with thirteen parameters, but then people will think you're kind of crazy. (I shouldn't go that far. There are a few cases where there may be a need for thirteen parameters, but they are very rare.)

Also, you can have the same number of parameters, if their types are different:

static int Multiply(int a, int b)
{
    return a * b;
}
 
static double Multiply(double a, double b)
{
    return a * b;
}

This works because the two Multiply methods each have their own signature (Multiply(int, int) and //Multiply(double, double)).

The following does not work:

static int Multiply(int a, int b)
{
    return a * b;
}
 
static int Multiply(int c, int d) // This won't work.  It has the same signature.
{
    return c * d;
}

The magic of method overloading is that you can have many methods that do very similar work on different types of data (like the int multiplication and the double multiplication above) without having to have a completely different method name. It makes things easier on the people calling the method (yourself, for starters, but the other people on your team as well). With different signatures, the C# compiler can determine which of the overloaded methods to use.

Revisiting the Convert, and Console Classes

With a basic understanding of how methods work in C#, it is worth a second discussion about some of the classes that we've already been using.

For instance, in the Console class, we've done things like this:

Console.WriteLine("Hello World!");

With our new knowledge of methods, we can see that here, we have the Console class, which has defined in it a method called WriteLine that we call. The WriteLine method has one parameter, which is a string. We pass in the string "Hello World!", and the method does what it's supposed to do, and away it goes.

This, though, is a perfect example of why we use methods. Because someone has already created a WriteLine method that writes stuff to the console window, we don't need to worry about how it does it. All we care about is that it does it, and that it is easy to use.

We see similar things with the Convert class, which we have used several times as well:

int number = Convert.ToInt32("42");

The Convert class has a ton of methods that let you convert many types to other types. In the example above, we see a method called ToInt32, which takes a string as a parameter. This class uses method overloading extensively, because it has a ToInt32 method that takes a string, as well as one that takes a double, as well as one that takes a short, and so on.

The Minimum You Need to Know About Recursion

There's one additional trick people use with methods that I think is worth bringing up here. It's kind of a complicated trick, so right now, you don't need to know all about it, but it is worth mentioning it, so you know what it is when it comes up, and so that hopefully, you can start thinking about how it might be useful.

The trick is called "recursion", and it is where you call a method from inside itself. Here's a trivial example (which happens to break on you when you run it):

static void MethodThatUsesRecursion()
{
    // This uses recursion, because the method calls itself.  
    // But with this simple example, you're going to have problems.
    // It will just keep going deeper and deeper, further down the line.
    // Kind of like that movie Inception.
    // When it gets too deep for C# to keep handling (too many layers)
    // it will give up and give you a "StackOverflowException".
    MethodThatUsesRecursion(); 
}

Like I said (repeatedly), this example is going to break on you, because it just keeps going into deeper and deeper methods. The problem with this first example, is that there is no "base case". It will never get to a point where it is done, and can start returning from the method calls, back up to the starting point.

A base case is a situation where there is no need to keep going further, and the method can simply return without "recursing" further.

Kind of the classic example of when you could use recursion is the mathematical factorial function. Perhaps you remember from math classes that the factorial, written as an exclamation mark by a number (like "7!") means 7 * 6 * 5 * 4 * 3 * 2 * 1. 72! Would be 72 * 71 * 70 * 69 * … * 3 * 2 * 1. (With factorial, by the way, you really quickly run into the overflow issues that we were talking about back in the more math tutorial.)

In this case, we know that 1! is 1. This can function as a base case. Every other number, say 7!, can be thought of as the number multiplied by the factorial of the number one less than it (7 * 6!).

This sets up a great opportunity for recursion:

static int Factorial(int number)
{
    // We establish our "base case" here.  When we get to this point, we're done. Both 0
    // and 1 are defined to have a factorial of 1. Mathematics doesn't define a factorial
    // for negative numbers, and we shouldn't allow it either, but we don't yet have the
    // tools to appropriately account for that. (See the Exceptions tutorial.)
    if(number == 0 || number == 1)
    {
        return 1;
    }
 
    return number * Factorial(number - 1);
}

One last thing is worth mentioning here, too. Every step must be getting you closer to the base case. Otherwise, you'll still never reach the end.

What's Next?

That wraps up our introduction to methods. Methods are extremely powerful, and we are going to be using them all through the rest of these tutorials (in fact, we've been using them, without knowing what they were, up to now).

We've seen how you can return values from a method, and pass information into methods as parameters.

We've also covered a couple of slightly more advanced topics with methods, including overloading and recursion. If you didn't fully grasp the concepts of those, don't worry too much—they are a bit more advanced, after all. But it is important that you be aware that they exist, and as we run into them again, you'll continue to become more familiar with them.

Next, we move into the world of "object-oriented programming"—a particularly poor buzzword that alludes to some very cool stuff in C#! Next stop: classes!