An Introduction to C# 11 Object-Oriented Programming

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 publicprivate, 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.


Categories