Classes Part 2: Creating Them

Classes Part 2: Creating Them

The Crash Course

  • Classes typically go in their own file (though C# doesn't require it, like Java does).
  • Variables can be added to a class—these are called instance variables.
  • You can add as many constructors to a class as you want.
  • You can also add methods to a class, which operate on the instance variables that the class has.
  • The private keyword, attached to a method or instance variable, means that only stuff inside the class can see it.
  • The public keyword, attached to a method or instance variable, means that anything (inside or outside of the class) can use it.
  • This tutorial also covers scope, name hiding, and the this keyword, all of which are very similar to C++ and Java.

Introduction

In the previous tutorial we learned what a class is, what an object is, and how to use them. Now, we'll move ahead and see how to create your own classes.

The ability to create your own classes is very important. This tutorial will give you a good understanding of the basics of creating your own classes—something you'll probably be doing a lot of.

By the way, I'm putting a complete project with the code that we write here at the bottom of this tutorial, so that in case you get hung up on a part of it, you can see a complete, working example.

In this tutorial, we'll create a simple Player class, which we could use to represent a player in a game.

I'll admit, there's a lot here. But don't give up yet! With our previous discussion about methods, and what we'll discuss here about classes, you'll really have all of the fundamental concepts of C# in your power!

Creating a New Class

We're ready to make our own class. Remember that a class works as a blueprint or template for what any of a specific kind of object needs to be able to keep track of and do.

While you are allowed to put multiple classes in a single file (unlike Java, which requires a single file for each class), it is typically good practice to put each new class you create into its own file. This helps keep the size of your files to a manageable size.

So our first step is to create a new file inside of our project. To do this, in the Project Explorer, right-click on your project. (The very top level item in the Project Explorer is your "solution", which is simply a collection of related projects. We've only got one project, which you choose the name of when you create a new project. So here, you'll basically be clicking on the second item in the Project Explorer, which is your project.) In the context menu that pops up, choose Add > Class….

creating-a-new-class-file.png

This will bring up the "Add New Item" dialog, with the Class template already selected. At the bottom, type in the name of your new class. I've called mine Player.cs, so my class will be called Player. When you've got the name ready, press the Add button, and your new file will be created.

When your new file is created, because you told it to make a new class, you'll see that Player.cs contains some default code that looks like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace CreatingClasses // Your namespace will likely be different here.
{
    class Player // If you called your class/file something different, that will show up here instead.
    {
    }
}

This starts things off pretty close to where we want it to be—we've got our new class started!

Adding Instance Variables

The first step of actually building our class is going to be adding some variables to our class. Remember that one of the key components to an object is that they keep track of their own data. These are stored as variables that belong to the object. Since each object, or "instance" of the class will have its own set of data, these variables are called instance variables. If you have two different players, they can each keep track of their own scores separately, each in its own instance variables.

For our simple player, we're going to keep track of the player's name, lives left, and their current score. To create an instance variable for each of these, we'll add the following code inside of the curly braces ('{' and '}') that are our class:

private string name;
private int score;
private int livesLeft;

So your current code should look something like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace CreatingClasses
{
    class Player
    {
        private string name;
        private int score;
        private int livesLeft;
    }
}

We start off with the private modifier, which we'll discuss more of in a second. The rest is just like creating any other variable—we state the type of variable, followed by the name for the variable. (All of the variables that we've created so far have been "local variables", with the exception of the ones that are created in a method definition, which are often called "parameters". So "instance variables" are the third type of variable that we've run into so far.)

Access Modifiers: private and public

So coming back to that private keyword that we see here. Everything in a class has what is called an "accessibility" level. This indicates where it can be accessed from. The private keyword illustrates that this variable can only be accessed (used or changed) from inside of the class. Anything outside of the class doesn't even know it exists.

Another type, which we'll see here, is public. The public access modifier means that it can be seen or used from outside of the class. It is an option to make these variables public, but that would mean that anyone, anywhere can directly modify them, which could have bad consequences. For instance, that would mean that someone could change livesLeft to a negative value, which wouldn't make sense.

Generally speaking, a class should be responsible for handling any changes to its instance variables—not anything outside of the class. This keeps everything that the class is responsible for protected inside of the class.

By the way, if you don't put an access modifier, these variables would be private by default—private is the default accessibility level for all members of a class. While it isn't necessary to specify that in this case, I think it is generally a good idea to put it in there, so that it is obvious to anyone who sees it that it is private.

There are about three other access modifiers that we haven't talked about yet. They are more advanced, and we can talk about them another time. For now, these two are all we need.

Adding Constructors

Now that we've got instance variables ready, let's add in a couple of constructors. When it comes time to make constructors, we usually want to look back at the instance variables we have and think about what kind of ways we want people to create a new object, and what they will want to immediately set. For instance, it probably doesn't make sense to create a player without a name. So we'll probably want to make a constructor that has a name as a parameter. We might also want to require a number of lives the player should start with. On the other hand, the player's score will nearly always start off at 0, whenever we create a new Player.

So for this particular class, we probably want a constructor that takes only a name as a parameter, and probably also one that takes both a name and a number of lives. So let's go ahead and add the following code into our class, just below our instance variables:

public Player(string name)
{
    this.name = name;
}
 
public Player(string name, int startingLives)
{
    this.name = name;
    livesLeft = startingLives;
}

So now, our full code looks like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace CreatingClasses
{
    class Player
    {
        private string name;
        private int score;
        private int livesLeft;
 
        public Player(string name)
        {
            this.name = name;
        }
 
        public Player(string name, int startingLives)
        {
            this.name = name;
            livesLeft = startingLives;
        }
    }
}

Notice that here, we use the public access modifier. We want people outside of the class to be able to create Player objects, so we need to make these public. (There are times that we might want to make constructors private or one of the types that we haven't discussed yet, but for now, all of the constructors we make will be public.)

All constructors that we create will have the exact same name as the class. So in this case, Player. In fact, that is how the C# compiler knows that what we are making is a constructor, as opposed to a normal method. In addition, we don't have a return type (we don't say public void Player() or anything) which is yet another key to the compiler that this is a constructor, instead of a method.

We then put the parameters for our constructor in parentheses, just like a method.

There are a few other important diversions this little code sample brings up, which we'll discuss in turn: variable scope, and the this keyword.

Variable Scope

One thing about variables that we haven't talked about yet is a thing called "scope" or "variable scope". The scope of a variable refers to the places in the code that a variable exists, or is valid—where you can refer to it and use it.

Let's say you create a method, like we've done before, and inside of it, you create a variable like this: int aNumber = 3;. This variable can be used anywhere inside of that method. However, you can't use that variable inside of another method. The scope of this variable is only the method that it was created in, not any other method.

Let's look at another example:

public void DoSomething()
{
    for(int x = 0; x < 10; x++)
    {
        Console.WriteLine(x);
    }
 
    x = -5;  // This won't compile--the variable 'x' is "out of scope".
}

In this method, we have a for loop. Inside of the for loop, we create a variable called x. This variable is only valid inside of the for loop—once we're outside of the loop, the variable no longer is valid. It is "out of scope". We won't be able to use it anymore.

In fact, though, we can create a second for loop, reusing the same variable name (x) for a different variable, and the two won't conflict:

public void DoSomething()
{
    for(int x = 0; x < 10; x++)
    {
        Console.WriteLine(x);
    }
 
    for(int x = 50; x < 60; x++)
    {
        Console.WriteLine(x);
    }
}

Whereas the first type of scope we saw was called "method scope", this type of scope is called "block scope", or "inner block scope".

The key here is to let the curly braces be your guide. Anything created inside of a set of curly braces can be used anywhere inside of those curly braces.

The next step out of this, is class scope, or class-level scope. If you look at the instance variables we created, they were put inside of the class's curly braces, and can be used anywhere inside of the class, including the constructors that we made, and the class's methods that we'll create in a minute.

Name Hiding

One interesting thing that you can do in C# is name a parameter or local variable (which has method scope or block sope) the exact same thing as something that has class scope. While making our class, remember that we made an instance variable called name, which has class scope (it's valid throughout the class). Then, inside of our constructor, we had a parameter that is also called name, which has method scope (it's valid only throughout the method/constructor).

This leads to a situation called "name hiding". While the instance variable name is usable throughout the class, including in the constructor, we now have another variable inside of the constructor that is also usable, with the exact same name. So when we refer to name inside of this constructor (or method), which of the two is it referring two? The one with the smaller scope—the one that was created in the method.

So we result in a situation where one variable "hides" another variable, based solely on the name.

That's all fine and good, but what if we want access to the variable with larger scope? (The instance variable, in this case.)

Well, there's a few ways to address this. One is, simply call the two variables different names, so there's no hiding happening. This, of course, is perfectly fine. For instance, we could have called the parameter playerName instead of name. But this becomes a bit of a hassle, to always be trying to think of similar but different names for the same variable.

A second approach, that is actually fairly common, is to name all of your instance variables in a specific way, so that you always know what you're referring to, while still being consistent. For instance, start all of your instance variable names with m_. So your instance variables would be m_name, m_livesLeft, and m_score. With this method, your names are always different from the variables that have method scope, just like in the first approach, but it is systematic, so you always know what belongs to what.

Let me be clear on this point: the m_ approach is fairly common, and a lot of people use it. But I, personally, don't like it. And let me take a brief second and explain why: it kills readability. One of the biggest challenges with programming is that it is much easier to write code than it is to read code. Anything we can do to make code more readable makes the code that much better. When you see all of these m_'s floating around, you have to mentally remove it to know what the variable does. And on top of that, in the cases where you're actually reading code out loud (possibly discussing it with another programmer) you always have to say "em underscore score" when you're referring to m_score. But like I said, this is commonly done, and people have their reasons for doing it (which are valid). Some of the best programmers I know use this, so if that's what you like, then go for it.

So there's a third approach here, which I, personally, prefer. This is the way that I'll be doing all of these tutorials. So if you like a different way, you'll have to change it over yourself. (And know that I'm shaking my head in discontent as you do so!)

This third approach is to name the instance variable and the local variable or parameter the same thing, allow name hiding to happen, and use the this keyword whenever you want to refer back to the instance variable. Which brings us to…

The this Keyword

Any time that you are inside of a class, you can get access to all of the things (methods and instance variables) that the class has by using the this keyword. this is a reference to the current object that you are inside of. So continuing with our example, any time you want, inside of your Player class, you can say this.name to get access to the name instance variable. This also works with methods, so you can call another method that belongs to the class by doing this.MethodName();.

In many cases, this isn't needed, because C# assumes you are referring to the stuff inside of the class, if there's not anything by that name in the method scope or block scope.

But connecting it all together, if you have name hiding going on, the this keyword allows you to get back to the instance variable with class scope, even though there's another variable or parameter with method scope that would otherwise be hiding it.

So looking back at the code we added:

public Player(string name)
{
    this.name = name;
}
 
public Player(string name, int startingLives)
{
    this.name = name;
    livesLeft = startingLives;
}

In both of these methods, there's a parameter with the same name as the instance variable (name) and we get access to the class's name variable using the this keyword. When we just say name, we're referring to the parameter instead, so when we say this.name = name;, we're taking the value of the name parameter that gets passed in to the constructor, and assign that value to the instance variable called name.

Well. That's enough of a detour. Let's get back to finishing our class.

Adding Methods

So now we need to figure out what methods our class might want to define. Perhaps we want a way to get the current player's name. We also might want to get the player's current score, and add points to the player. We also might want a way to "kill" the player and make him or her lose a life, and also check to see how many lives the player has.

We'll add methods for each of these with the following code:

public string GetName()
{
    return name;
}
 
public int GetScore()
{
    return score;
}
 
public void AddPoints(int totalPoints)
{
    score += totalPoints;
}
 
public void Kill()
{
    // We make sure they can't get negative lives.
    if (livesLeft > 0)
    {
        livesLeft--;
    }
}
 
public int GetLivesLeft()
{
    return livesLeft;
}

So now our completed Player class should look like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace CreatingClasses
{
    class Player
    {
        private string name;
        private int score;
        private int livesLeft;
 
        public Player(string name)
        {
            this.name = name;
        }
 
        public Player(string name, int startingLives)
        {
            this.name = name;
            livesLeft = startingLives;
        }
 
        public string GetName()
        {
            return name;
        }
 
        public int GetScore()
        {
            return score;
        }
 
        public void AddPoints(int totalPoints)
        {
            score += totalPoints;
        }
 
        public void Kill()
        {
            // We make sure they can't get negative lives.
            if (livesLeft > 0)
            {
                livesLeft--;
            }
        }
 
        public int GetLivesLeft()
        {
            return livesLeft;
        }
    }
}

There's a few things that ought to be discussed with this new code. In the past, when we've created methods, they've been marked static. We'll talk about that more in a second.

First, though, you'll note that all of these methods are marked public. That means that anyone outside of the class (anyone who creates an instance of our Player class) can call these methods. We could have marked these methods private if we had wanted to, in which case, you could only call the method from another method inside of the class. In our specific case, we wanted everyone to be able to call these methods, so we made them public.

Second, we see here several methods that start with "Get". It is very very common to have methods that more or less just return the value of an instance variable, and another one that sets the value of the variable (possibly checking to make sure that the value you are assigning is actually valid). So you may have lots of "GetSomething" methods and "SetSomething" methods. For now, that's what we'll stick with. But this is so common, that C# provides a feature in the language that makes this much more readable and simple to use (called "properties") which we'll talk about in a later tutorial.

So now going back to that static keyword…

Static Methods, Variables, and Classes

So far, in this tutorial, we've been working with variables (instance variables) and methods that belong to an instance of the class. Meaning you can create multiple instances of the class, and they'll all have their own set of data. And when you call the methods, they'll do work using their own set of variables.

In previous tutorials, though, we've marked our methods static. So let's talk about what that means. Any class-level variable or method that is marked with the static keyword belongs to the class as a whole, rather than any particular instance.

For a variable, this means that any instance that uses it, it will be the exact same value. If instance #1 changes a static class variable to the value of 4, then instance #2 will also see that change. The variables are shared between all instances of the class.

For a method, this means that you don't need an instance of the class to call it, since it belongs to the class.

To explain, a "normal" method that isn't static might be accessed like this:

Player player = new Player("Bob Dole");
player.AddPoints(100);

A method marked with the static keyword is accessed directly, using the class name. You don't need to create an instance of the class to use the methods it has. We saw this with the Console class:

Console.WriteLine("Hello World!");

WriteLine is a static method, so we don't need to create a Console object (an instance of the Console class) in order to use the WriteLine method.

One final note: you can also use the static keyword on a class. If you do this, this means that the class cannot have instance variables or methods. They must all be static, and the C# compiler will check this for you. The Console class is one example of this—it is impossible to create an instance of the Console class, but then, there's no need to do so, because all of its methods are static.

Finishing the Sample

OK, there's one more final thing we want to do here before we wrap up this tutorial: make sure we see how to use our new Player class.

This class will work very much like what we saw in the previous tutorial about using classes. In your Main method, you can create an instance of your Player class, and work with it like this:

Player player = new Player("Frankenstein");
 
while (player.GetLivesLeft() > 0)
{
    player.AddPoints(100);
 
    player.Kill();
}

You can find a complete project with this Player class created and working here: CreatingClasses.zip

What's Next?

Wow. Perhaps you're a bit overwhelmed now. There's a lot there. But between methods and classes, we've actually covered the most important stuff in C#. Everything else we'll be doing from here on out is going to be more fluff than anything. So if you've made it this far, you've done well!

Our next topic will be properties, and then we'll start doing some stuff with reading and writing to files, and progress from there to some of the other more advanced C# features.