Hello again, and welcome to one of the more important concepts you’ll need to know about. We’ll be talking about classes and structs; one of which you’ve already unknowingly used. We’ll also probably briefly touch on some object oriented programming (OOP) concepts due to the nature of classes and such. As always,if it’s bold, it’s a concept or term you should do more research on outside of this lesson.
Accessibility
Quick note before we head straight into classes, we need to talk about the accessibility keywords. The three main important ones are public, private, and protected. We’re just going to cover public and private; protected is for inheritance which we’ll cover a bit later.
You can add these keywords on to variables, classes, functions, etc. They determine what can or cannot be accessed. Anything inside a class can access all the other functions inside that same class. However if you’re referencing an outside class, you can only access that class’ public information.
You’ll see examples of these in use below, but that’s the general idea. Items marked as public can be accessed outside the class, while if they’re private they cannot be. If you do not give a variable or function a direct accessibility keyword, it defaults to being private.
Classes
Classes are used extensively in most programs, if you’ve setup our basic console program then you’re already using a class. By default it’s probably just called Program
, but we can make our own for all kinds of different purposes. The idea of classes is creating objects to hold different data. When you make a class, you can then create an instance of it, and have access to that data and be able to manipulate that data. You can also have variables or functions inside a class not bound by an instance, those are static. We’ll talk about static in a later tutorial.
If that confused you, don’t worry I’m gonna give some examples as best I can. I think classes are much easier understood through example, and you can compare making classes with real life objects. To make a class, it’s pretty simple. You simply define it like so:
class Example { }
See? Easy peasy. Now inside this class is where you can put your functions, variables, etc. Lets get some examples going. Let’s say you want to have some kind of user database, where you can add new users, see current user information, etc. That would be an ideal situation to make a User
class that stores all that information. It might look like this.
class User { string name; int age; }
This gives us some basic information about the user, and we can add as many variables as we like. In our main function, we could create an instance of this class.
User myFirstUser = new User();
It’s that simple to make a variable instance of our class. Now let’s say we wanted to access name or age, we could set them or show them to the console. If we tried to type myFirstUser.name
it wouldn’t come up with anything. That’s because of accessibility, as mentioned before. Since we didn’t give them a keyword, right now they’re private and we’re not allowed to access them. If we change them to public however (Change string name
to public string name
for example) we can get those variables and change them!
This concept of having private and public variables, and determining what can or cannot be accessed is known as encapsulation. Some people don’t understand why we might use it, and just make everything public. But that’s a terrible idea for organization purposes, especially when working on a team. Do some more research (aka google it) on the benefits of encapsulation for more examples about why it should be used.
Now that we can make our own class, we can use it like any variable. We could make a list of them, like List users = new List()
, to keep track of all the users we have.
Constructors
You may notice when we make our new class, we set it using = new User()
and those parenthesis are empty. When we make a class without a constructor it has a default empty constructor. However we can give it a custom one that allows us to pass in information with some parameters. Remember, parameters are variables we can have a function take in and then give values to when we call. To do this, we would do like so:
class User { public User(string userName, int userAge) { name = userName; age = userAge; } string name; int age; }
You’ll see that this is just like a function, the only difference is that it doesn’t have a return type. That’s because its return type is the instance of the class, so we cannot give a different one. Making a custom constructor can be useful for passing initial data we need when making the class. Keep in mind, if you make a custom constructor it will override the default empty constructor. You won’t be able to do = new User()
, you’ll only be able to use the constructor you defined.
This isn’t a problem however. You can make as many constructors as you want, you could have one that takes in nothing (Like the default one), it could only take in age, or only take in the name.
You can also make a destructor, which does the opposite. You can only have one of these, but it gets called when the object is destroyed. You create those very similarly to constructors, but with a ~
in front. This can be used to cleanup old data you don’t want lying around.
public ~User() { Console.WriteLine("A user has been destroyed!"); }
Inheritance
Inheritance is a big part of classes, and object oriented programming in general. The general idea is having one class be another class and have all the same variables as it, while also having its own identity. This is normally known as a parent child relationship. I again feel like this is easiest to explain and understand via examples.
Let’s say you’re making an RPG that has different classes: Knight, Wizard, and Rogue. They each have different things unique to them, a knight might have melee attacks, while the Wizard would have a mana bar, and the rogue would have… I don’t know a stealthly meter?
But they all also have some similar things, a name, maybe an age, a health bar, a stamina bar. Instead of making a class for each and copying over the health variable, age, etc. we’ll use inheritance.
class Role { public string name; public int health = 100; public int stamina = 100; int damage; }
Here is a basic class that each of our RPG classes would need. To make our RPG class derive (become a child of) our Role class, we simple add : Role
after the declaration like so:
class Wizard : Role { public int mana = 100; }
If we made an instance of Wizard Wizard ourWizard = new Wizard()
and tried to access its variables, it would have not only mana, but also health, stamina, and a name. Additionally the Wizard is also technically a Role, we could do Role mainPlayer = new Wizard()
and it would work just fine, because a Wizard is a Role. We’ve made the Wizard a child of Role. Keep in mind Role cannot access anything in Wizard, and Role is not a Wizard.
Note in C# you can only inherit from one class in C#, in C++ it allows you to inherit from multiple classes on one class.
Protected
Now we get to briefly talk about the protected keyword, similar to public and private it determines what classes can see what information. Protected is a special keyword just for the child parent relationship. Let’s say in our Role class we wanted a variable only our Wizard could use, for the sake of the example let’s say its a string secret
. We don’t want anyone outside to know the secret, however the Wizard should be able to see it. We can make this variable protected string secret
and only children of Role will be able to access that variable.
Virtual/Override
We can also allow a relationship between our parent and child with functions. Let’s say you have a function in your parent called Attack(Role enemy)
, which as expected would damage your player. This works fine for an average player, but what if we wanted our Wizard to not only lose health, but also mana? Well it would be a bit silly to have a AttackAndLoseMana(Role enemy)
function. Plus it might be confusing.
What we can do is mark our Roles function as virtual and then override that function in our Wizard class. We would have:
class Role { public virtual Attack(Role enemy) { enemy.health -= damage; } } class Wizard : Role { public override Attack(Role enemy) { enemy.health -= damage; mana -= someValueAmount; } }
Here marking virtual makes the function available to be overriden by a child. If nothing is overriding it, then the parents function will be called. If you call Attack on the Wizard class, it will run the wizards version instead.
Well you’ll notice we’re also having to write health -= amount
again in the function. That’s not a big deal here, but what if we had more complex logic in our Role’s version that we wanted to use. Instead of rewriting health -= amount
we can call the Roles Attack function by using base.Attack(enemy)
. This will call the classes direct parents version of the function; where base is accessing the parent.
There’s a lot of other stuff you can do with virtual and override, and inheritance in general. You can have multiple levels of inheritance, you could have a FireWizard
class that derives from Wizard for example. There are also pure virtual functions that would make your class into an abstract class. These are really some good subjects to do some googling on to learn more about in depth.
Another Example
If these examples confused you, here’s another brief one. Lets say you’ll have a game with a lot of different types of weapons. You might have something like this.
class Weapon { public int damage; public virtual void Attack(Role someEnemy) { someEnemy.health -= damage; } } class Gun : Weapon { int reloadSpeed = 2; float fireRate = 0.5f; int ammo = 100; public override void Attack(Role someEnemy) { ShootBullet(someEnemy); } void ShootBullet(Role someEnemy) { //Handles making a bullet and shooting it } } enum ResourceType { Iron, Steel, Diamond } class Sword : Weapon { float swingSpeed; public ResourceType swordMaterial; public override void Attack(Role someEnemy) { SwingWeapon(someEnemy); } void SwingWeapon(Role someEnemy) { //Swings the weapon based on swing speed } }
Value vs Reference
Value and reference types can be tricky, and when you define them they’re created in different areas in memory. Value types are allocated on the stack, while reference types are created on the heap.
What I mean by this is where the data for those variables are stored in memory. There are two places, the stack and the heap. The stack is for static memory and retrieval of the information is very fast, but it is much smaller than the heap. The stack is used when you know how much data you will need at runtime. The heap on the other hand is for dynamic memory, and is much larger than the stack; but retrieving it’s data is slower.
You’ve probably already used value types, they’re the variables like ints, bools, etc. While reference types are like Strings or your own classes you’ve created.
The difference is when you’re accessing a value type, you’re changing the data value of that variable directly. While when accessing a reference, you’re just getting a reference to the value, but it’s not actually the value. It’s like knowing the location of the data and mailing a postcard telling it to change the value (reference), rather than just going to that address and changing it yourself (value).
Structs
With that information, we move on to structs. They’re almost exactly the same as classes in C# with various differences. The biggest of which is that structs are a value type, unlike when you make a class instance (which would be a reference) when you make a struct, its a value type. Structs also do not support inheritance. You can find more minor differences by searching online, and good cases to use a struct over a class.
Interfaces
Interfaces are a great tool for enforcing certain classes/structs implement a function or property. They essentially act as a contract for that class/struct, ensuring it has to implement that function. If it doesn’t, you’ll get an error and won’t be able to continue. You add these to a class like you would a base class, but unlike base classes you can have as many as you want.
You do not give a access modifier in the interface, but it should be public when implemented.
Traditionally interfaces start with an I
, and then whatever it’s for.
interface IHealth { int GetMaxHealth(); void LoseHealth(); void GainHealth(); } public class Player : BaseClass, IHealth { int health; public Player() { health = GetMaxHealth(); } public int GetMaxHealth() { return 10; } public void LoseHealth() { health--; } public void GainHealth() { health++; } }
Extra Practice
From now on I’m gonna stop making entire word documents for these practices, and instead just outline them here. For this tutorial specifically we covered a lot of ground, and there’s not a lot I want you to specifically focus on. Instead I think you should look into making a mini RPG system, similar to what we tried to do with the Wizard and Role classes above.
Have the player be able to start up the application and input some basic info shared between all classes, such as a name, an age, etc. Then allow them to choose a specific class, you don’t have to use mine, but give the player a few options. Then the player should be able to see all the stats of these characters. They should be able to add or remove new characters, and see stats from any characters they’ve made before.
If you’re looking to get really adventurous, look into saving and loading data with C#. (Hint, look up FileStream, StreamReader and StreamWriter)
Next Time
We’re learning about the static keyword!
Support
Are you having trouble with understanding this tutorial? Please feel free to contact me via email at KoseckCory@gmail.com or message me on discord at 7ark#8194.
I would love to get feedback from people so I can add and improve these tutorials overtime. Letting me know what you’re confused about will let me update the tutorials to be more concise and helpful.
If you’re interested in supporting me personally, you can follow me on Twitter or Instagram. If you want to support me financially, I have a Patreon and a Ko-fi.
Very helpful clear and precise . Thank you. I even want to bookmark this