Creating Objects with Classes

Objects

C# is an “object-oriented” language, meaning that the programming logic is based on the thinking that everything is an object. Every time you initialize a variable, you create an object to store a value. When you create a new list, you are creating an object to hold other objects.

If you were creating a video game, some of the objects for that application would be a player object, an item object, an enemy object, a location object, and so on.


Every object has attributes that make it unique, as well as attributes that are the same for all objects of that type. In our video game example, every enemy may have a unique name but may have the same amount of life points. You may sometimes hear the value of the attributes be referred to as the “state” of the object.


Every object also has actions it can do or actions that can affect its attributes. The player object could move to a new location, or the enemy object could take damage to its life points.


Creating Classes

A class is not an actual object. A class is a definition of how the object of that type will look. It determines what attributes the object will have, as well as what actions it can do or can be done to it. Think of the class as a blueprint of the object. Every object created from the class will have the same attributes and behaviors.


Items in a class are often called the “members” of the class. This post will go into greater detail on the following member types:


Fields – these are the variables of that will be used by the methods of the class

Properties – public members that are used to abstract the value of the fields

Methods – actions and calculations that can be done by the class

Constructors – unique methods used to create an object instance of the class


The syntax for creating, also called “defining,” a class follows.


[Access Modifier] [Class Name]

{

[Code Block}

}


Example:

 public class Player
 {
 
 }

This example shows the minimal amount of code that you will need to define a new class. Technically, you don’t even need the access modifier. If you don’t provide a modifier, the default modifier “internal” is used.


For this post, we are going to stick with the internal modifier since it makes the most sense for what we will be discussing. You will learn more about “public,” “protected,” and “abstract” classes in other posts.

 class Player
 {
 
 }

Of course, this class doesn’t help since it doesn’t give a good blueprint of how a player object would look. The player needs attributes and actions. Before we give our player class some attributes, let’s talk about the differences between fields and properties.


Fields

As mentioned earlier, fields are the variables that will store information about the object. They store data that other members of the class will need to execute their code. Fields are typically declared at the top of the code block for the class. Field naming conventions are similar to the name convention for method variables. Their purpose should be specific, and they should utilize camel casing.


For example, our Player class would potentially have fields that store values for the player’s name, their starting life points, their current life points, and their character type.

 class Player
 {
 string playerName;
 int startingLifePoints;
 int currentLifePoints;
 string characterType;
 } 

It is good practice to make your field inaccessible with the “private” assess modifier then using properties to read or update the values of the field. If you don’t use an access modifier, the default access is private. This process is called “data hiding” and is one of the pillars of object-oriented programming, which requires hiding the inner workings of the class from any entity creating and object from the class.


Properties

Properties provide a public way to access or update the values in the fields. They allow you more flexibility when reading, updating, or computing the values in your fields. Properties typically share the same name as the field for which they provide functionality. The only difference is that the property uses pascal casing instead of camel casing.

 public string PlayerName
 {
 
 }

Properties consist of two unique methods called “accessors.”


The GET accessor is utilized when you want to return the value of the field. The GET accessor is similar to a method with no parameters that returns the value of the property.

 public string PlayerName
 {
 get { return playerName; }
 }

Properties allow you to customize the retrieval of a field’s value. There will be some times you want to provide more information than just the value of a single field.

 string firstName;
 string lastName;
 
 public string FullName
  {
 get 
 {
 return $"{firstName} {lastName}";
 }
 }

This property combines the values of the two fields into a single return value.

Sometimes, you will use a property to only return part of the field’s value. A perfect example of this would be a system that stores a customer’s social security value but only allows the last for digits to be displayed.

 string ssn;
 public string SSN
 {
 get
 {
 return ssn.Substring(ssn.Length - 4, 4);
 }
 }

You can also use properties to perform calculations on a field before returning the value.

 DateTime birthday;
 
 public int Age
 {
 get
 {
 return DateTime.Today.Year - birthday.Year;
 }
 }

You use the SET accessor to provide a new value to the field. The SET accessor is a void method with a hidden parameter named “value.” This hidden parameter will contain the value the user of the class is attempting to assign to the field.

 public string PlayerName
 {
 get
 {
 return playerName;
 }
 set
 {
 playerName = value;
 }
 }

Properties allow you to make any adjustments or perform calculations with the value being passed in before setting the field’s value. The following code automatically removes a corruption percentage every time the savings amount is increased.

 double corruptionAmount;
 double savingsAmount;
 public double SavingsAmount
 {
 get
 {
 return savingsAmount;
 }
 set
 {
 corruptionAmount += value * .10;
 savingsAmount = value - corruptionAmount;
 }
 }

Properties also allow you to put protective measures around the updating of the fields to ensure accurate information is being inputted into the field. The following example will stop a field from receiving a value that does not match its purpose.

 int studentAge;
 public int StudentAge
 {
 get
 {
 return studentAge;
 }
 set
 {
 if (value > 13 && value < 19)
 {
 studentAge = value;
 }
 }
 } 

If this application is being used to track high school students, the students would be between the ages of 14 and 18.


Throw Statement

Ideally, we should let the user of the class know that they have entered invalid data into the property so they can fix their code to stop it from happening. Since there is no user interaction between a class and the developer using the class, the best way to let them know they did something wrong is to THROW an exception.


Hopefully, you have read our post on Exception Handling, so you understand about exceptions. What that post didn’t cover is that you can create your exception at any time. You can then “throw” that exception. When you throw an exception, the application will treat it like any other exception. It will terminate the process flow with an error unless that exception is caught and handled somewhere in the process.


When creating a class, you throw exceptions to let any developer using your class know they have done something that would break the functionality of your class. That developer must catch and handle your exception.


If we look at our code example from earlier, the SET accessor does not tell the developer that they have provided an invalid input for our student’s age. We should remedy this by throwing an exception when this occurs. The throw an exception, use the following syntax.


THROW keyword NEW keyword [ExceptionType] ([Error Message]);


Our new code would look like this:

 int studentAge;
 public int StudentAge
 {
 get
 {
 return studentAge;
 }
 set
 {
 if (value > 13 && value < 19)
 {
 studentAge = value;
 }
 else
 {
 throw new ArgumentException("Value must be between 14 and 18");
 }
 }
 }

Read-Only Properties

Sometimes you will want to restrict applications using your class to only reading the value of a property. You want to remove the ability to change the value. To accomplish this, you would remove the SET accessor from the property.


For example, your class may handle payroll information for your employees, and you have some rules around overtime.


The following code anyone using our class to get the standard overtime rate and the maximum number of overtime hours, but they can never change those values.


 private int maxOverTimeHours = 25;
 public int MaxOverTimeHours
 {
 get
 {
 return maxOverTimeHours;
 }
 }
 
 private double standardOverTimeRate = 1.5;
  public double StandardOverTimeRate
 {
 get
 {
 return standardOverTimeRate;
 }
 }

Read-only properties are widespread with application development.


Write-Only Properties

Although they are not nearly as frequent as read-only properties, write-only properties do exist. To create a write-only property, you would remove the SET accessor. Our social security example from earlier would be better written as a write-only property.

 string ssn;
 public string SSN
 {
 set
 {
 ssn = value;
 }
 }

Auto Properties

If you are creating a property and not planning to add any functionality to the GET or SET accessor, you can use an “automatic property” instead of declaring a field and coding the default GET and SET functionality. When you create an automatic property, Visual Studio creates a hidden field and abstracts away the functionality of the GET and SET accessor.

The following code demonstrates how to create automatic properties.

 public string CharacterName { get; set; }
 
 public int LifePoints { get; set; }
 
 public bool HasDied { get; set; } = false;

The “HasDied” property declaration demonstrates how you would set a starting value to the underlying field.


Auto properties can also be used when creating read-only properties as well. The following code shows our StandardOverTimeRate and MaxOverTimeHours as auto properties.

 public int MaxOverTimeHours { get; } = 25;
 public double StandardOverTimeRate { get; } = 1.5;

Class Methods

As I discussed earlier in this post, an object has behaviors, actions that it can do, or that is done to the object. The functionality for these actions is coded as methods. There is another blog post to teach you all about the different types of methods you can use in a class.

However, there a few concepts that increase in importance once you start creating classes.


Access Modifiers

As I mentioned earlier, classes have a default access modifier of “internal,” meaning everything within the same project can access that class. Class methods have a default access modifier of “private.” Only code within the same class can access a private method. If you want developers using your class to have the ability to execute the code inside your methods, you will need to make sure those methods are “internal,” as well.


You will, more than likely, want a mix of private and internal methods in your class. The internal methods will call the private methods at the right time in the right order. This process allows you to hide some of the detailed steps your process makes and ensures they are executed in the correct order. This concept is called “abstraction.” The following example shows some method structures that may exist in a banking application.

 internal void TransferFunds(double requestedAmount, string fromAccount, string toAccount)
 {
 VerifySufficientFunds(requestedAmount, fromAccount);
 WithdrawFunds(requestedAmount, fromAccount);
 DepositFunds(requestedAmount, toAccount);
 }
 
 private void DepositFunds(double requestedAmount, string toAccount)
 { }
 
 private void WithdrawFunds(double requestedAmount, string fromAccount)
 { }
 
 private void VerifySufficientFunds(double requestedAmount, string fromAccount)
 { }

In this example, we have three private methods that can not be accessed outside of the class. This code allows us to make sure they aren’t called out of order.

I will talk about the other concepts later in the post, but first, I need to teach you about creating an object from your class.


Instantiating Objects

Once the class has been written, you have a blueprint that tells you exactly what attributes and behaviors an object created from that class would have. The act of creating an object from a class is known as “instantiating the object.” This process is also known as “creating an instance of the class.” Regardless of what phrase you use, the result is that you have an object in your application with properties that can be accessed and updated and methods that can be executed.

The following code shows an Enemy class that may be used in a video game.

 class Enemy
 {
 public string EnemyType { get; set; }
 public int HitPoints { get; set; } 
 public int Strength { get; set; }
 
 private void Heal()
 {
 HitPoints = HitPoints + 10;
 }
 
 internal bool DetermineBattleResults(int heroStength)
 {
 if (heroStength > Strength)
 {
 HitPoints = HitPoints - (heroStength - Strength);
 if (EnemyType == "mage")
 {
 Heal();
 }
 }
 bool isDead = HitPoints <= 0 ? true : false;
 return isDead;
 }
 }

This class is our blueprint for any enemies we want to create in our game. It has three attributes (EnemyType, HitPoints, Strength). There are two actions (Heal, DetermineBattleResults). Every enemy we create will use this blueprint.


Create an Object

It is time to create some enemy objects.

To create an object, you must declare the variable. The variable will have a type that matches the class you want to use as a blueprint. The following example declares a variable with an Enemy type.

 Enemy firstEnemy;

To create the object, you must use the NEW keyword.

 Enemy firstEnemy = new Enemy();

Once you do that, you can start setting values to its attributes.

 firstEnemy.EnemyType = "barbarian";
 firstEnemy.HitPoints = 100;
 firstEnemy.Strength = 20;

You also call the methods of the object. You will not be able to call the Heal method, since that method has a PRIVATE access modifier.

 int playerStrength = 50;
 bool isEnemyDead = firstEnemy.DetermineBattleResults(playerStrength);

The strength of classes comes into play when you need multiple objects of the same type. You can use the same blueprint for each one of them.

 Enemy secondEnemy = new Enemy
 {
 EnemyType = "mage",
 HitPoints = 50,
 Strength = 10
 };
 
 Enemy thirdEnemy = new Enemy
 {
 EnemyType = "ranger",
 HitPoints = 75,
 Strength = 15
 };

You can even create a collection of your object type.

 List<Enemy> gameEnemies = new List<Enemy>();
 gameEnemies.Add(firstEnemy);
 gameEnemies.Add(secondEnemy);
 gameEnemies.Add(thirdEnemy);
 gameEnemies.Add(
 new Enemy()
 {
 EnemyType = "goblin",
 HitPoints = 60,
 Strength = 12
 });

Although each one of these objects was created from the same blueprint, they are different “instances” of the class.


Before I go into more detail regarding using an object’s properties and methods, I want to go back to the creation of class to discuss constructors.

Constructors

A constructor is a unique method of a class that is only used when creating a new object of that class type. The constructor method has a name that matches the name of the class and has no return type.

 public Enemy()
 {
 
 }

You call the constructor by using the NEW keyword when creating a new object of your class type.

 Enemy firstEnemy = new Enemy();

If you have been coding along with this post, you may have noticed that we were able to create a new Enemy object without actually writing a constructor method. Visual Studio provides a default constructor every time you create a new class. Just as the auto properties abstract away their functionality, the default constructor is also hidden.

Custom Constructors

There will be many occasions where you want to perform specific actions or require specific data every time an object of your class type is created. For these scenarios, you can write a custom constructor. Earlier, we created several Enemy objects. Every time we created a new Enemy, that object did not have a value for the type, hit points, or strength. We wrote extra code to populate that data, but ideally, that information should be required every time we create a new Enemy.


To enforce the setting of these values, we can change our constructor to require them. To do this, we add parameters to the constructor that would hold those values.

 public Enemy(string type, int hitpoints, int strength)
 {
 EnemyType = type;
 HitPoints = hitpoints;
 Strength = strength;
 }

Once you write a custom constructor, the default constructor disappears, so the only way to create a new Enemy is to use your constructor. Just like any other method, once parameters are declared, arguments are required when the method is called. If you try to use the default constructor now, you will get a compilation error stating


“There is no argument given that corresponds to the required formal parameter ‘type’ of ‘Enemy.Enemy(string, int, int)'"


To create a new Enemy object now, we must provide the required arguments.

 Enemy firstEnemy = new Enemy("barbarian", 100, 20);
 Enemy secondEnemy = new Enemy("mage", 50, 20);
 Enemy thirdEnemy = new Enemy("ranger", 75, 15);
 Enemy fourthEnemy = new Enemy("goblin", 60, 12);

Multiple Constructors

When creating a class, you have the opportunity to create multiple ways that an object of your class type can be instantiated. This is accomplished by having multiple constructors, also known as “constructor overloading.” Overloading a method means you have several methods with the same name, but the parameters for each method are different.

For example, if we wanted a way to create a default Enemy type without providing any arguments, we can “overload” our constructor by creating one that has no parameters but sets the property values.

 public Enemy()
 {
 EnemyType = "bandit";
 HitPoints = 80;
 Strength = 20;
 }

Now the users of our class have two options when instantiating a new Enemy object. They can provide the values by passing the values into the parameters of the first constructor, or they can use the second constructor and accept our default values.


Using an Object

Once you have created an object of your class type, you can it’s properties the way you would any other object you have learned about so far.

 Console.WriteLine(“You must battle the following enemies.);
 foreach (Enemy item in gameEnemies)
 {
 Console.WriteLine($"A {item.EnemyType} with a strength of {item.Strength}.");
 Console.WriteLine($"This {item.EnemyType} has {item.HitPoints} hitpoints remaining.");
 }

Object Methods

You can also use the methods of the object as long the accessor modifiers of those methods allow it. To use methods of an object, you type the name of the object followed by a period then the name of the method. It is more than likely that you have already done that while learning to write code.

 string nameOne = "Scott", nameTwo = "scott";
 if(nameOne.Equals(nameTwo))
 DateTime hireDate = DateTime.Today;
 DateTime benefitDate = hireDate.AddDays(30);

In this code I am using the methods of the STRING and DATETIME object types. I can use the methods of the Enemy object as well.

 int playerStrength = 100;
 bool isEnemyDead = firstEnemy.DetermineBattleResults(playerStrength);
 if (isEnemyDead)
 {
 Console.WriteLine($"You defeated the {firstEnemy.EnemyType}!");
 }

Static Class

A static class is a class that has a private constructor, so it cannot be instantiated as an object. Static classes are often used to group a collection of methods that were created to perform actions or provide calculations based on input parameters, not class properties. These classes allow you to utilize the functionality of the methods without creating a new object.


If you have been coding along with the posts on this blog, you have been using a static class. Type this code into Visual Studio:

 Console myConsole = new Console();

When you do, you get a compilation error stating: “Cannot create an instance of the static class ‘Console’


The Console class is static. It exists to provide you with methods to interact with the console window.


You can use any of the public methods of a static class by using the class name and the method.

 Console.ReadLine();
 Console.WriteLine("Display Text");

Static classes tend to contain helper methods that provide functionality unrelated to any object. The Math class is a static class that provides you with methods to perform mathematical functions.


Create a Static Class

To create a static class, use the STATIC keyword before the class name. The following example will be a class to store methods that aid us in formatting text we are reading and writing to the console window. It will be a Console Formatting Library, CFL, for short.

 static class CFL
 {
 
 
 }

Remember, using the STATIC keyword will make the constructor private, forbidding the instantiation of the class into an object.


Every method inside a static class must also have the STATIC keyword. The following method will get the input from a Console.Readline call, then trim the whitespaces and convert it to lower case. The method will return the resulting string to the caller of the method

 public static string GetInputTrimmedLower()
 {
 return Console.ReadLine().Trim().ToLower();
 }

The next method does the same thing but only trims the whitespace.

 public static string GetInputTrimmed()
 {
 return Console.ReadLine().Trim().ToLower();
 }

This method receives a string argument and will display the text in the console using a red color.

 public static void DisplayRedText(string text)
 {
 Console.ForegroundColor = ConsoleColor.Red;
 Console.Write(text);
 Console.ForegroundColor = ConsoleColor.Gray;
 }

Utilizing Static Methods

Now that we have our Console Formatting Library complete, we can use its functionality wherever we want in our application without having to create a new object.

 Console.WriteLine("What is your name?");
 string userName = CFL.GetInputTrimmed();
 
 CFL.DisplayRedText($"Hello {userName}. Would you like to play a game (y/n)?");
 string userInput = CFL.GetInputTrimmedLower();
 
  if (userInput == "n")
 {
 Environment.Exit(0);
 }

90 views0 comments

Recent Posts

See All