Get up to speed in this chapter on object-oriented programming in C# with details on declaring classes, methods, fields, and properties. Topics covered include adding methods and properties to a class and creating, initializing, and finalizing class instances.
So far in this course, we have looked at the basics of programming in C#, such as variable types and flow control. Although writing a functional program using these techniques would be possible, there is much more to becoming a proficient C# programmer. C# is, above all, an object-oriented programming language, and as such, any C# programmer will be expected to create object-oriented applications using this language.
C# provides extensive support for developing object-oriented applications. This lesson will introduce the basic concepts involved in object-oriented programming and then explain the concept related to application development using C#.
What is an object?
An object is a self-contained piece of functionality that can be easily used and re-used as the building blocks for a software application.
Objects consist of data variables (properties) and functions (methods) that can be accessed and called on the object to perform tasks. These are collectively referred to as members.
What is a class?
Much as a blueprint or architect’s drawing defines what an item or a building will look like once it has been constructed, a class defines what an object will look like when it is created. It defines, for example, what the methods will do and the member variables.
Declaring a C# class
Before an object can be instantiated, we first need to define the class blueprint for the object. In this lesson, we will create a Bank Account class to demonstrate the concepts of C# object-oriented programming.
A C# class is declared using the public class keywords followed by the name of the class:
public class BankAccount { }
We have now defined a class that currently contains no members. The next task, therefore, is to add some members.
Creating C# class members
Class members, fields or, properties are essentially variables and methods embedded into the class (the concept of methods is covered in the next lesson). Members can be public, private, or protected.
Public members can be accessed from outside the object and are also visible in classes derived from the current class. Private members can only be accessed by methods contained in the class and are not accessible to derived classes. Protected classes are only available to derived classes.
This is the key to what is called data encapsulation. Object-oriented programming convention dictates that data should be encapsulated in the class and accessed and set only through the methods of the class (typically called getters and setters).
When implementing data encapsulation, it is important to understand the difference between fields and properties within C# class declarations.
Fields vs. properties
A member of class that is directly declared as a variable is referred to as field. In the following class, or example, the firstname
variable is declared as a field:
public class MyClass { public string firstname; // This is a field }
This is referred to as public field. When declared in this way, code outside of the class can directly access this variable and make changes to it.
Object-oriented programming guidelines, however, dictate that fields should generally be protected from direct access )known as private field). In C# this is achieved by declaring a property which, in turn, provides access to the underlying private field.
A property is provided with a name and declares special get
and set
methods (referred to as accessor methods) which are used to provide access to the underlying field using the following syntax:
public string <property name> { get { return <field name>; } set { <field name> = value; } }
In the case of the get
method, the code simply returns the value assigned to the field. The set
method, on the other hand, is passed a variable named value
which can be assigned to the field.
Using this approach, we can now implement a property to encapsulate our firstname
field:
public class MyClass { private string _firstname; public string FirstName { get { return _firstname; } set { _firstname = value; } } }
Note: When a field is associated with a property, the field should be declared as being private, and the name is generally prefixed with an underscore (_)
Whether or not to encapsulate all fields within properties and have no public fields is a matter of personal preference in terms of how rigidly you want to adhere to object-oriented programming conventions. Even in Microsoft’s C# reference documents, you will see examples of both approaches. For the sake of brevity, we will be making use of public fields in many examples in this course.
Adding members to the class
We can now extend our ‘BankAccount’ class to add fields to hold the account name and number. True to the concept of data encapsulation we will be making these fields private and using properties to provide access:
public class BankAccount { private string _accountName; private int _accountNumber; public string AccountName { get { return _accountName; } set { _accountName = value; } } public int AccountNumber { get { return _accountNumber; } set { _accountNumber = value; } } }
Now that we have defined some fields and properties for our class we need to look briefly at a few additional data member types, and then learn how to create object instances from the class.
Static, read-only, and const data members
In addition to the data member types we have looked at so far, C# also provides support for a number of additional member types.
C# static member types (also referred to as class properties) are used to store data values that are common to all object instances of the class. For example, all bank customers would likely earn the same rate of interest on a savings account. An interestRate
member would, therefore, be declared as static since it is common across all object instances of the class. Static members are declared using the static
keyword. For example:
public class BankAccount { public static int interestRate; }
Static members are accessed through the class, not through the object. For example, we would change the interestRate
member for all object instances by reference the BankAccount class as follows:
BankAccount.interestRate = 10;
For data members that must not be modified the const
and readonly
keywords are provided by C#. Both achieve the same objective of preventing the value assigned to a data member from being changed after it has been declared. The value of a const
or readonly
member must be assigned at creation time:
public readonly daysInWeek = 7;
Instantiating an object from a C# class
The process of creating an object from the class ‘blueprint’ is called instantiation. The first step is to create an object variable of the required object type. An instance of the object is then created using the new
keyword and assigned to the object variable:
BankAccount custAccount; custAccount = new BankAccount();
It is also possible to declare the object variable and assign the object in a single statement:
BankAccount custAccount = new BankAccount();
Now that we have an instance of our BankAccount
class the next step is to learn how to access the members of the class.
Accessing C# object members
Now that we know how to write a class and instantiate objects from the class, we now need to know how to access the members of the object.
First, you will recall that we declared some members as being public
and others as being private
. The public methods are fully accessible from outside the object. This is achieved using something called dot notation.
Dot notation is a mechanism by which object members may be accessed by specifying the object and member names separated by a dot (.
). For example, to access the accountName member of an object named custAccount
we would reference this member using custAccount.accountName
.
The following code demonstrates the creation of an instance of our BankAccount
class and the use of dot notation to access the public member properties of the object:
class Demo { public class BankAccount { private string _accountName; private int _accountNumber; public string AccountName { get { return _accountName; } set { _accountName = value; } } public int AccountNumber { get { return _accountNumber; } set { _accountNumber = value; } } } static void Main() { BankAccount custAccount = new BankAccount(); custAccount.AccountName = "John Smith"; custAccount.AccountNumber = 53211; System.Console.WriteLine($"Customer Name is {custAccount.AccountName}"); System.Console.WriteLine($"Account Number = ${custAccount.AccountNumber}"); } }
After creating an instance of our BankAccount
class, the above code assigns values to two private fields via the AccountName
and AccountNumber
properties of our object. The code then references the properties in order to display the customer name and account number values.
Adding methods to a C# class
The methods of a class are essentially code routines that can be called upon to perform specific tasks within the context of that class.
Methods come in two different forms, static methods (also referred to as class methods) and instance methods. Static methods operate at the level of the class, such as creating a new instance of a class. Instance methods, on the other hand, operate only on the instances of a class (for example performing an arithmetic operation on two member variables and returning the result).
Instance methods are declared within the opening and closing braces of the class to which they belong and are declared using the standard C# method declaration syntax.
Static methods are declared in the same way as instance methods with the exception that the declaration is preceded by the static
keyword.
For example, the declaration of a method to display the customer’s name in our example class might read as follows:
public class BankAccount { private string _accountName; private int _accountNumber; . . . public void displayName() { System.Console.WriteLine($"Customer name is {AccountName}"); }
The method is an instance method so it is not preceded by the static
keyword and can be used as demonstrated in the following example:”
BankAccount custAccount = new BankAccount(); custAccount.AccountName = "John Smith"; custAccount.DisplayName();
When designing the BankAccount
class it might be useful to be able to call a static method on the class itself to identify the maximum allowable balance that can be stored by instances of the class. This would enable an application to identify whether the BankAccount
class is suitable for storing details of a new customer without having to go through the process of first creating a class instance. This method will be named getMaxBalance
and is implemented as follows:
public class BankAccount { . . static double GetMaxBalance() { return(10000000.00); } }
With the method declared, it can be called directly on the class without the need to create an instance as follows:
double maxBalance = BankAccount.GetMaxBalance(); System.Console.WriteLine($"Maximum allowed balance = ${maxBalance}");
Now that we have looked at method class members the next task is to look at two special class methods known as constructors and finalizers.
C# constructors and finalizers
Despite the grand-sounding names, C# class constructors and finalizers are nothing more than methods that get called when an object is instantiated and destroyed. The constructor is particularly useful for allowing initialization values to be passed through to an object at creation time. Let’s say that we would like to be able to initialize the _accountName
and _accountNumber
members at the point that we initialize the custAccount
object. To do so we need to declare a constructor.
Constructors are declared the same way as other methods with the exception that the name of the method must match the class name, for example:
public class BankAccount { private string _accountName; private int _accountNumber; // Constructor public BankAccount(string accountName, int accountNumber) { _accountName = accountName; _accountNumber = accountNumber; } // .... }
We can now use the constructor to initialize these members at the point that the instance is created:
BankAccount custAccount = new BankAccount("Fred Wilson", 123456); System.Console.WriteLine ($"Customer name = {custAccount.AccountName}"); System.Console.WriteLine ($"Account number = {custAccount.AccountNumber}");
Finalizers are used to clean up any resources used by a class object when the object is destroyed. Unlike constructors which can be triggered from code using the new
keyword, there is no way to explicitly call a finalizer (for example there is no delete equivalent to the new
keyword). Instead, the finalizer will be called when the runtime system decides that the object instance is no longer needed. All the programmer can be sure of is that the finalizer will be called at some time between when the object is no longer needed by the code and the point that the application terminates.
Finalizers are defined in the same way as constructors with the exception that the name is preceded by the tilde character (~
):
// Finalizer public ~BankAccount(string acctName, int acctNumber) { // Code to perform clean up }
The this
keyword
When constructing classes in C# you may encounter situations where a property that is local to a method, or the name assigned to the method parameter conflicts with a class property. This can be a particularly common occurrence when using public fields within class declarations.
Take, for example, the following sample class in which the parameter names for the initializer match those of the public fields:
When you run the above code, the output will indicate that Mark is 0 years old even though his age was set to 37 when the class instance was created. In addition, the compiler will issue warnings with read:
warning CS1717: Assignment made to same variable; did you mean to assign something else?
The problem here is that the compiler doesn’t know which name
and age
variables are being referenced in the following initializer code:
public Demo(string name, int age) { name = name; age = age; }
To resolve this problem, we need a way to tell the compiler that the method parameter values are to be assigned to the corresponding public field values. We can do this using the this
keyword. In this context, the this
keyword represents the current instance of a class. To differentiate the conflicting name
and age
variables we need to use this
keyword as follows:
class Example { class Demo { public string name; public int age; public Demo(string name, int age) { this.name = name; this.age = age; } } static void Main() { Demo demo = new Demo("Mark", 37); System.Console.WriteLine($"{demo.name} {demo.age} years old"); } }
Now when we run the code, not only do we no longer see the warning messages from the compiler, but we also get Mark’s age correct.