Hello! Welcome back to another magical post about Object Oriented Programming with C#.
This post will introduce the concept of constructors and how they are helpful in creating objects.
The main topics discussed are:
- Default values
- Default constructors
- Constructor overloading
- The 'this' keyword
- The 'base' keyword
Assuming you have already read my post about C# classes and objects, let's move on to the wizard examples!🧙🏻♀️
Constructors
First, it's important to answer the question: what is a constructor?
- A constructor is a special method in a class that sets up and initializes a new object, ensuring that all its properties are given the right starting values.
If you want more info about constructors, it's beneficial to get a few different examples & points of view. Here are some good resources to learn more:
- W3Schools constructors - free course
- C# Constructors - article
- Codecademy's C# Classes & Objects - free course
- Microsoft's C# Docs
Wizard Class without a constructor
Remember our "Wizard" class from the last post? If not, that's ok, here it is:
public class Wizard
{
private int age;
// Properties
public string Name { get; set; }
public string House { get; set; }
public string Pet { get; set; }
public int Age
{
get
{
return age;
}
set
{
if (value >= 11 && value <= 150)
{
age = value;
}
else
{
Console.WriteLine("Invalid age for a wizard at Hogwarts.");
}
}
}
public string WandType { get; set; }
// Methods
public void CastSpell(string spellName)
{
Console.WriteLine($"{Name} casts {spellName}!");
}
}
In this example, if we wanted to add a new "Wizard" we would create a new "draco" object like this:
Wizard draco = new Wizard
{
Name = "Draco Malfoy",
House = "Slytherin",
Age = 11,
WandType = "Hawthorn with a unicorn hair core",
Pet = "Eagle Owl"
};
But there's an easier way! and that's why we use constructors.
Wizard class with a constructor
Here's how the Wizard class would look with a constructor:
public class Wizard
{
private int age;
public string Name { get; set; }
public string House { get; set; }
public string Pet { get; set; }
public int Age
{
get
{
return age;
}
set
{
if (value >= 11 && value <= 150)
{
age = value;
}
else
{
Console.WriteLine("Invalid age for a wizard at Hogwarts.");
}
}
}
public string WandType { get; set; }
// constructor
public Wizard(string name, string house, int age, string wandType, string pet)
{
Name = name;
House = house;
Age = age;
WandType = wandType;
Pet = pet;
}
// Method to represent casting a spell
public void CastSpell(string spellName)
{
Console.WriteLine($"{Name} casts {spellName}!");
}
}
This makes it much easier to create a new "Wizard" aka a new object:
Wizard draco = new Wizard("Draco Malfoy", "Slytherin", 11, "Hawthorn with a unicorn hair core", "Eagle Owl");
Default values
But what if we don't know everything about a new "Wizard" at Hogwarts?
Could we just, not enter that info?
Well, if we did that, this is what would happen.
First, we have our constructor:
public Wizard(string name, string house, int age, string wandType, string pet)
{
Name = name;
House = house;
Age = age;
WandType = wandType;
Pet = pet;
}
Then we try to create a new Wizard:
Wizard neville = new Wizard("Neville Longbottom", "Gryffindor", 11, "Cherrywood with a unicorn hair core");
// Missing the 'pet' parameter
So.. what happens if we don't include Neville's pet toad?
We will see this error 😱
error CS7036: There is no argument given that corresponds to the required formal parameter 'pet' of 'Wizard.Wizard(string, string, int, string, string)'
So.. how can we avoid this? 🙄
Well, that's what default values are for!
// look! some default values!
public Wizard(string name = "Unknown", string house = "Unassigned", int age = 11, string wandType = "Standard", string pet = "Unknown")
{
Name = name;
House = house;
Age = age;
WandType = wandType;
Pet = pet;
}
This way, if we choose to omit, or forget to assign a value, the code will still run with the value being assigned to the default value.
Default constructors
Another important topic is default constructors although they sound similar to default values, they serve a different purpose.
They are the simplest form of constructors which don't take any arguments.
In some cases, a default constructor might set properties to their default values, or it might leave them to be set later.
For example, if you want to create a "Wizard" but you don't have all the necessary info, a default constructor allows you to instantiate the object and fill in the details later:
public Wizard()
{
// Initialize with default values
Name = "Unknown";
House = "Unassigned";
Age = 11; // Default starting age for Hogwarts students
WandType = "Standard";
Pet = "Unknown";
}
Then you can create a new "Wizard" like this:
Wizard ginny = new Wizard();
Since "ginny" is a new student, we still don't have all of her details. They must be assigned as they become known to the school.
// When Ginny is enrolled, we know her full name
ginny.Name = "Ginny Weasley";
// Then we assign the rest of the properties as we become aware of them
// After the sorting hat ceremony we find out she is sorted into Gryffindor!
ginny.House = "Gryffindor";
// Then she must register her wand
ginny.WandType = "Yew with a phoenix feather core";
// Later, when she gets a pet, we would update her pet info
ginny.Pet = "Arnold the Pygmy Puff";
But this seems a bit tedious, why would I ever use this method over the previously described methods?
Well, a default constructor can be particularly useful when you need to create an object without immediately supplying detailed information, it provides the ability to set properties at a later time.
This is exactly what we did with "ginny", we didn't have all of the information at first so we had to add the new information as we became aware of it.
Constructor overloading
Similar to default values and default constructors, there is something called constructor overloading. We do this when we might not have all the details about a wizard, or maybe we have wizards with different sets of known attributes.
So.. what's the difference between all three?
Default values and default constructors are useful when you want to fill in missing information automatically. However, constructor overloading makes it possible to create objects with varying sets of initial information from the very beginning.
public class Wizard
{
public string Name { get; set; }
public string House { get; set; }
public string Pet { get; set; }
public string WandType { get; set; }
// Intentionally excluding Age for readability
// Constructor for a wizard without a pet
public Wizard(string name, string house, string wandType)
{
Name = name;
House = house;
WandType = wandType;
}
// Constructor for a wizard with a pet
public Wizard(string name, string house, string wandType, string pet)
{
Name = name;
House = house;
WandType = wandType;
Pet = pet;
}
// Constructor for a foreign wizard where we only know the name and wand type
public Wizard(string name, string wandType)
{
Name = name;
WandType = wandType;
// House and pet might be unknown
}
}
// Wizard with a pet
Wizard hermione = new Wizard("Hermione Granger", "Gryffindor", "Vine wood with a dragon heartstring core", "Crookshanks");
// Foreign wizard
Wizard viktor = new Wizard("Viktor Krum", "Hornbeam");
Can you use default values, default constructors, and constructor overloading at the same time?
Yes! At the end of this post, there will be an example of an ultimate "Wizard" class using everything mentioned in this post.
'this' keyword
What's this?
Well, the "this" keyword has two main usecases.
1. To avoid confusion ( this.Name = name; )
public class Wizard
{
public string Name { get; set; }
// Constructor using the 'this' keyword to differentiate between
// the parameter 'name' and the class property 'Name'
public Wizard(string name)
{
this.Name = name; // 'this.Name' refers to the property 'Name' of the current instance
// Name = name; can get confusing, so using this.Name can be a bit clearer sometimes
}
}
2. Constructor chaining ( : this(name) )
public class Wizard
{
public string Name { get; set; }
public string House { get; set; }
public string WandType { get; set; }
// Constructor that takes only Name
public Wizard(string name)
{
this.Name = name;
}
// Constructor that takes Name and House, reusing the first constructor
public Wizard(string name, string house) : this(name)
{
this.House = house;
}
// Constructor that takes Name, House, and WandType, reusing the second constructor
public Wizard(string name, string house, string wandType) : this(name, house)
{
this.WandType = wandType;
}
}
What's the point of constructor chaining?
Constructor chaining allows a constructor to call another constructor within the same class to set things up, which keeps the code simple and avoids repetition. Neat, right?
'base' keyword
Another keyword?? 🤨
Yes.. but this one is pretty easy!
It's very similar to the "this" keyword, except it can help chain info from other classes.
According to our previous examples, the "Wizard" class represents all students or faculty members at Hogwarts. However, students may also participate in school sports, like Quidditch, which requires keeping track of additional details like the player's position.
So we would also need to create a "QuidditchPlayer" class that extends the Wizard class because it needs to include all the standard wizard information, plus these extra sports-related attributes.
This is how that would look:
// Base class for any wizard at Hogwarts
public class Wizard
{
public string Name { get; set; }
public string House { get; set; }
public Wizard(string name, string house)
{
Name = name;
House = house;
}
}
// Derived class for a Quidditch player
public class QuidditchPlayer : Wizard
{
public string Position { get; set; }
// Constructor sets up a Quidditch player, using 'base' to include the basic wizard setup info
// The 'base' keyword passes name and house to the Wizard constructor
public QuidditchPlayer(string name, string house, string position) : base(name, house)
{
Position = position;
}
}
QuidditchPlayer harry = new QuidditchPlayer("Harry Potter", "Gryffindor", "Seeker");
That's it for basic constructors. Here are all of the things we learned together in an ultimate "Wizard" class!
ULTIMATE WIZARD CLASS!
public class Wizard
{
public string Name { get; set; }
public string House { get; set; }
public string WandType { get; set; }
// Default constructor
public Wizard()
{
Name = "Unknown";
House = "Unassigned";
WandType = "Standard";
}
// 1. Constructor that takes only Name but inherits the defaults from the default constructor
public Wizard(string name) : this()
{
Name = name;
}
// 2. Constructor that takes Name and House, reusing constructor 1
public Wizard(string name, string house) : this(name)
{
House = house;
}
// 3. Constructor that takes Name, House, and WandType, reusing constructor 2
public Wizard(string name, string house, string wandType) : this(name, house)
{
WandType = wandType;
}
}
public class QuidditchPlayer : Wizard
{
public string Position { get; set; }
// Default constructor for QuidditchPlayer
public QuidditchPlayer() : base()
{
Position = "Chaser";
}
// Constructor chaining is used here as well, reusing Wizard's constructor
public QuidditchPlayer(string name, string house, string wandType, string position) : base(name, house, wandType)
{
Position = position;
}
}
// A lesser-known wizard with minimal information
Wizard blaise = new Wizard("Blaise Zabini");
// A member of the Order of the Phoenix with some details known
Wizard mundungus = new Wizard("Mundungus Fletcher", "Gryffindor");
// A fully detailed wizard
Wizard hermione = new Wizard("Hermione Granger", "Gryffindor", "Vine wood with a dragon heartstring core");
// Quidditch player with all properties known
QuidditchPlayer harry = new QuidditchPlayer("Harry Potter", "Gryffindor", "Holly wood with a phoenix feather core", "Seeker");
// Example output:
Console.WriteLine($"Wizard: {blaise.Name}, House: {blaise.House}, Wand: {blaise.WandType}");
// Output: Wizard: Blaise Zabini, House: Unassigned, Wand: Standard
Console.WriteLine($"Order Member: {mundungus.Name}, House: {mundungus.House}, Wand: {mundungus.WandType}");
// Output: Order Member: Mundungus Fletcher, House: Gryffindor, Wand: Standard
Console.WriteLine($"Wizard: {hermione.Name}, House: {hermione.House}, Wand: {hermione.WandType}");
// Output: Wizard: Hermione Granger, House: Gryffindor, Wand: Vine wood with a dragon heartstring core
Console.WriteLine($"Quidditch Player: {harry.Name}, House: {harry.House}, Wand: {harry.WandType}, Position: {harry.Position}");
// Output: Quidditch Player: Harry Potter, House: Gryffindor, Wand: Holly wood with a phoenix feather core, Position: Seeker
Challenge (optional)
If you're up for it, I challenge you to make your own constructor that includes:
- Default values
- A default constructor
- Constructor overloading
- The 'this' keyword
- The 'base' keyword
Theme ideas:
- Astronomy: Classes for celestial objects like Star, Planet, and Asteroid. Properties could include mass, composition, and orbit details.
- Magical Beasts: A MagicalBeast base class with properties like rarity, danger level, and magical abilities. Subclasses could include Dragon, Phoenix, Basilisk, each with special traits or abilities.
Don't be afraid to ask your friendly neighborhood chatGPT for more ideas that align with your personal interests😄
If you actually do the challenge, please send me the link to your code. I would love to see your solution!
Well, that's it for this post! If you've made it this far, Hermione certainly would be proud of you.
In the future, I will be making part 2 which will be about "static constructors", "copy constructors", and "destructors". (Thought it would be a bit overwhelming to add those here)
If you're looking for more resources to learn how to code check out my Free Coding Resources site, it has links to a bunch of my favorite courses/resources!
If you have any questions or want to connect, the best place to reach me is on Twitter/X.
Happy coding!