This course takes code from the Data Types and Variables course and refines it using object-oriented (OO) principles. We explore some of the main concepts of OO programming during this process, such as encapsulation, code reuse, and inheritance.
Along the way, we learn more about essential code structures such as conditional evaluation with if-then-else statements, functions for grouping code that performs a specific task, and for-loops for dynamically repeating an action. We will also look at how .NET and C# have object-oriented baked-in as a fundamental design principle underpinning the framework and language.
Learning Objectives
- Understand the benefits of object orientation and what came before it
- Learn about essential code structures and turn code into a class
- Refine the class code and learn more object-oriented concepts
- Learn about inheritance, a fundamental object-oriented concept
- Understand how object-orientation is a foundation principle of C# and .NET
Intended Audience
This course is intended for those who already have an understanding of data types and variables in C# and now want to learn about object-oriented principles.
Prerequisites
To get the most out of this course, you should have an understanding of C# as well as basic data types: strings, numbers, and Booleans. In order to follow along with the demos, you should also have a working development environment, whether in Windows, Linux, or macOS.
Resources
The GitHub repository for this course can be found here.
Inheritance is one of the main pillars of the object-oriented paradigm. What does this actually mean? In practical terms, inheritance is a way to reuse functionality for related classes. It's creating a new class from an existing class, where the new class inherits all the properties and functionality of the parent class and adds new properties and functionality. An often quoted example of inheritance is the relationship between a person, an employee, and a manager. A person has a date of birth, is of a gender, and has a place of residence. An employee has all the properties of a person and has an employee number, a physical work location, and a salary. A manager is an employee with specific responsibilities who has a collection of other employees to manage.
Let's look at the syntax and mechanics of applying inheritance to our checkerboard example. Now, I'm not much of a board games kind of guy, but let's say our board class is the generic ancestor or parent of boards for specific games. I want to create a chessboard class based on the board class. I'll make a new folder called boards that correspond to the boards namespace and then create a new file called chessboard.cs, and I'll paste in the first few lines from the boards file. Let's rename board to chessboard and say that the class is derived from board by putting a colon followed by board, the class we are inheriting from. The chessboard class will be the same as the board class, except that it will always be eight rows by eight columns. I will create a constructor for the chessboard class and tell it to execute the constructor of its immediate parent with a colon and the keyword base after the constructor name. As the board class has two constructors, it is the one with the matching parameter signature that is called. We need to hard code columns equals eight and rows equals eight for the chessboard class, and there are several ways we can do this, each demonstrating an aspect of inheritance. First, I'll just set columns and rows to 8. I don't want to use the public rows and columns properties for the simple reason that they should never be accessed in the case of a chessboard.
As you can see, the private rows and columns members are inaccessible to the chessboard class, which is absolutely correct as private members are only visible to the class they are defined in. I can redefine those members as being protected, which means they are only visible to the class they are defined in and all classes that inherit from that, i.e., children of the board class. Once I change those to protected, then I can use them in chessboard. Going back to program.CS, I'll create a new instance of a chessboard by changing new board to new chessboard with no parameters. I'll also get rid of the commented lines setting rows and columns. Another aspect of inheritance is that I can create an instance of a class and assign it to a variable that is the data type of the parent class. Here I'm creating a new chessboard, but I'm assigning it to a board.
Naturally enough, I can create a chessboard as a new chessboard type, but I can't create a board and assign it to a chessboard type because the parent, board doesn't know everything about its child, chessboard. This is not dissimilar to being a parent of teenagers. Let's have a look at another more elegant way of achieving the same result. I'll start by getting rid of board.Setup, return rows and columns to private, and remove the associated assignment statements from the chessboard constructor. Next, I'll call the parent constructor with two parameters and pass in eight for rows and columns. The parent or base constructor executes before the constructor of the current class. This will allow you to execute the default behavior of the parent and then override it if necessary in the child. There you go, very simple and clean, but there is still one problem. Class constructors aren't inherited, so I can't create a new chessboard instance with the two-parameter board signature, but public properties and methods are inherited as well as protected ones. This means you could create a chessboard and then use the rows and columns properties of the parent board class to override the dimensions. When I do that, we get an unsatisfactory result. First of all, we get the message saying that three columns are invalid, which is fine, but because columns were initialized to 8 from within the chessboard class, we get eight columns printed. At best, you could say this is a little inconsistent.
One way around this problem is to override the rows and columns properties in the chessboard class. Properties can only be overridden when they are either defined with the keywords virtual or abstract. I won't go into the differences between virtual and abstract right now so as not to overwhelm you, but in summary, virtual means the properties exist and are usable within the parent class, while abstract means they are placeholders to be implemented in child classes. Having marked those two properties as virtual, I can replicate them in the child chessboard and mark them as overridden with the override keyword. Next, I'll change the getter to return eight and change the setter to do nothing by leaving the set braces empty. When I run it now, the values assigned to rows and columns go nowhere, leaving the private rows and columns unaffected. Just to prove that the board class remains un-impacted by these changes, I'll rerun the program, creating a new board and explicitly setting rows and columns.
Hallam is a software architect with over 20 years experience across a wide range of industries. He began his software career as a Delphi/Interbase disciple but changed his allegiance to Microsoft with its deep and broad ecosystem. While Hallam has designed and crafted custom software utilizing web, mobile and desktop technologies, good quality reliable data is the key to a successful solution. The challenge of quickly turning data into useful information for digestion by humans and machines has led Hallam to specialize in database design and process automation. Showing customers how leverage new technology to change and improve their business processes is one of the key drivers keeping Hallam coming back to the keyboard.