Class Properties and Constructors

In a previous chapter, we added the MapLevel and MapSpace classes to our game project and, in this chapter, we’ll add some class properties and the code to actually create objects from those classes. Properties and constructors are basic elements of C# classes and this chapter shows examples of how they’ll be used to describe and create the dungeon maps in the program. Along the way, you’ll see examples of overloaded constructors and auto-properties in C# that provide for more flexibility in coding.

This post is part of a series on creating a roguelike game in C#. For the latest chapters and more information, please visit the Official Project Page on ComeauSoftware.com. The current code for this project is also available on Github.

Constructors

When the game creates a map for a new level, it will use a line like this:

MapLevel newLevel = new MapLevel();

That line declares a new variable of the MapLevel type and sets it to a new instance of the MapLevel class, i.e. a MapLevel object. In official language, that’s called instantiation.

Often, when a new object is created from a class, the class needs to carry out some actions like setting initial properties. Classes in C# and other languages have a special type of method for this called a constructor. It’s not absolutely necessary to have a constructor in C#; if you don’t add one then C# will generate a basic one in the background. It’s good practice, however, even if you don’t have anything specific for the constructor to do. Constructors can also accept arguments and use them to change the initial settings of the object as you’ll see later.

In the last chapter, we added an array definition to the MapLevel class to hold the map contents. Immediately after that declaration, add the following code:

public MapLevel()
{

}

Remember that the current code for this project is also available on Github.

Different languages have different ways of declaring a constructor. In C#, it’s simply a method that has the same name as its class. Notice that the constructor is declared as public, which means it can be called from outside the class, actually from anywhere inside or outside the project. This is necessary so that the class itself can be accessible to the rest of the project, even if some of the stuff inside it isn’t. The parentheses at the end of the first line mark it as a method and could also hold argument definitions for the arguments I mentioned earlier. As before, the braces define the limits of the constructor’s code.

Your code should now look like this:

Notice that the constructor now has a reference count shown above it. This is the number of places within the rest of the code where the class is being called from and it’s a handy little feature in Visual Studio. The MapSpace class has two references to it, namely the array definition that we added earlier. At some point, you might create functions and methods that fall out of use as the code is revised. This can be a useful way of identifying and removing this obsolete code.

We’ll be adding code to this constructor later but first, let’s move on to the MapSpace class.

Properties

Before we add the MapSpace constructors (yes – more than one), we need to add the class properties those constructors will reference. In the chapter on classes, I mentioned that classes have properties to hold information such as the character stats and item names. Each property needs to be declared within the class and there are a few ways to do this.

Visual Studio provides some quick ways to do this and one is called an auto-property.

  1. Position the cursor within the MapSpace class brackets.
  2. Type “prop” and press TAB twice.

The word you typed will change to:

public int MyProperty { get; set; }

This is the most basic syntax for a property within C#. Properties are a type of method that are generally declared as public so they can be referenced from outside and they have the get and set accessors that you see within the brackets. These enable the code to read and write to the object property.

For example, if you have a class for the player’s character with a property to represent the player’s current hit points, the code could read the HP or change it using these get and set accessors.

The auto-property assumed that the new property was supposed to be an integer so it set that by default but that’s not what we need. Change your new property to read as follows:

public char MapCharacter { get; set; }  // Actual character on map.

This is the property that will hold the character as it should actually exist on the map. Notice you can add a comment after any line to clarify what it does. The two forward slashes tell the C# compiler to ignore the rest of the line.

We need four more properties at this point. Again, you can use the “prop” and TAB x 2 shortcut to enter them. If you press TAB again after the property is created, you can move the cursor through the parts of the declaration to change them as needed.

public char DisplayCharacter { get; set; }  // Character actually displayed.
public bool SearchRequired { get; set; } // Does the player have to search for this?
public int X { get; set; }
public int Y { get; set; }

Some things in the game, like room exits and traps, have to be searched for with a specific key before the player will see them so the SearchRequired boolean (True / False) property specifies this. I also decided to store the X and Y map coordinates within the MapSpace object for reference so we now have two integer properties

Going back to the MapLevel class, we’ll just add one property right after the declaration for the levelMap array.

public MapSpace[,] LevelMap
{
    // Make map available to other classes.
    get { return levelMap; }
}

This property is actually declared as an array and functions as the public access to the levelMap array that we declared in a previous chapter. Notice that the get accessor has brackets after it and uses the return keyword to return the private array to whatever code is calling it. There is no set accessor so this is a read-only property. Code outside the class is not allowed to directly change the array object itself although the information in the individual array elements can be changed.

Constructor Overloading

Now that the properties are in place, complete the first constructor immediately below them:

public MapSpace()
{
    // Create blank space for map
    this.MapCharacter = ' ';
    this.DisplayCharacter = ' ';
    this.SearchRequired = false;
    X = 0;
    Y = 0;
}

This is a very basic constructor that takes no arguments and simply sets the object properties to default values – in this case, blank or 0. It might be used to create a blank space in which the properties can be changed later.

The next constructor has a bit more to do:

public MapSpace(char mapChar, int X, int Y)
{
    // Create visible character
    this.MapCharacter = mapChar;
    this.DisplayCharacter = mapChar;
    this.SearchRequired = false;
    this.X = X;
    this.Y = Y;
}

C# has a feature called overloading in which methods, including constructors, can have multiple versions so long as each version takes a different set of arguments. This is handy if you want a method or function to have different options depending on what information is available when it’s called.

Notice the this keyword in the above code; it refers to the properties of the object being created from the class. This can be useful if the parameters in the constructor declaration have the same names as the properties being set but it’s good practice to use it either way.

In this overload, the constructor accepts a specific character and the integer X and Y coordinates and then creates an object with those settings. The MapCharacter and DisplayCharacter are both set to the character that’s passed in. A call to this constructor might look like this:

MapSpace newSpace = new MapSpace(VERTICAL, 62, 12);

This example uses the VERTICAL constant to create a new MapSpace with a vertical wall character (║) at the coordinates 62, 12. You can see how the constant makes it easier to enter and read the code.

Here’s the next overload:

public MapSpace(char mapChar, MapSpace oldSpace)
{
    this.MapCharacter = mapChar;
    this.DisplayCharacter = mapChar;
    this.SearchRequired = oldSpace.SearchRequired;
    this.X = oldSpace.X; this.Y = oldSpace.Y;    
}

This overload of the constructor actually accepts a character and an existing MapSpace object and uses the old MapSpace to supply the settings for the new one, only replacing the character itself.

Also notice that the last line of code is actually two lines, separated by a semi-colon. Yes, you can combine lines like this so long as there is a semi-colon between them.

One more …

public MapSpace(char mapChar, Boolean hidden, Boolean search, int X, int Y)
{
    this.MapCharacter = mapChar;
    this.DisplayCharacter = hidden ? ' ' : mapChar;
    this.SearchRequired = search;
    this.X = X;
    this.Y = Y; 
}

This constructor explicitly sets all properties from the arguments passed in. The second line sets the DisplayCharacter and has some syntax you haven’t seen yet. When the question mark is used this way, it’s testing whether the hidden boolean parameter is True and then choosing a response from the two options that follow.

This is called the ternary operator and you’ll see it used at other times because the alternative code would be the folllowing IF statement:

if(hidden)
  this.DisplayCharacter = ' ';
else
  this.DisplayCharacter = mapchar;

Whether there’s any performance benefit to the ternary operator is debatable and, if there is, it’s so infinitesimal as to make no difference. It does make the code slightly more concise and readable, though.

The below images shows this constructor being called in code you’ll be entering later:

You can see how the Intellisense finds the four constructors and displays their signatures, or the arguments required. The dropdown window shown here is also displaying a number of the constructors as they are chars that would be appropriate for the first parameter.

Exploring Further

At this point, we’ve completed the MapSpace class, at least for now. As we progress through the game, we might need other properties or methods for it. Now we’re ready to start creating the map itself. In the next chapter, we’ll add more code to support that process.

If you haven’t done so already, I recommend taking some time to explore the various operators in C#. If you have a sandbox project setup for testing, it’s worth taking some time to try them out and get familiar with how they work.

Microsoft – C# Operators and Expressions

Next –


C# 13 and .NET 9 – Modern Cross-Platform Development Fundamentals: Start building websites and services with ASP.NET Core 9, Blazor, and EF Core 9
  • Explore the newest additions to C# 13, the .NET 9 class libraries, and Entity Framework Core 9
  • Build professional websites and services with ASP.NET Core 9 and Blazor
  • Enhance your skills with step-by-step code examples and best practices tips
ComeauSoftware.com uses affiliate links through which we earn commissions for sales.

Sign up for our newsletter to receive updates about new projects, including the upcoming book "Self-Guided SQL"!

We respect your privacy and will never share your information with third-parties. See our privacy policy for more information.

×