Skip to content

Classes

In the previous level of programming, you learned that you can define your own data types. These data types are complex, meaning they typically store more than one piece of data and contain functionality for using the data. Classes are fundamental building blocks of object-oriented programming (OOP). A class is a blueprint or template that defines the structure and behavior of objects. Objects are instances of classes, and they encapsulate data (attributes) and methods (functions) related to a specific concept or entity. The purpose of classes in C# is to facilitate the principles of OOP, which include encapsulation, inheritance, and polymorphism.

Anatomy of a Class

Classes typically consists of several components, each serving a specific purpose.

Class Declaration

The class declaration defines the structure and name of the class. It includes the class keyword followed by the class name and an optional base class (if any).

C#
namespace ADEV.Education
{
    public class Student
    {

    }
}

Fields (Attributes)

Fields are variables that store data within the class. They define the properties or attributes of objects created from the class.

C#
namespace ADEV.Education
{
    public class Student
    {
        // Fields
        private string name;
        private int identificationNumber;
    }
}

Constructors

Constructors are special methods responsible for initializing the state of objects when they are created. They have the same name as the class.

C#
namespace ADEV.Education
{
    public class Student
    {
        private string name;
        private int identificationNumber;

        // Constructor method
        public Student(string name, int identificationNumber)
        {
            this.name = name;
            this.identificationNumber = identificationNumber;
        }
    }
}

Methods

Methods contain the behavior or actions that objects created from the class can perform. They define the operations associated with the class.

Accessor and Mutator Methods

Accessor and mutator methods, also known as getter and setter methods, are used to control access to the private fields of a class. Accessor methods retrieve the value of a field, while mutator methods modify or set the value of a field. These methods are crucial for implementing encapsulation, a fundamental principle of object-oriented programming.

C#
namespace ADEV.Education
{
    public class Student
    {
        // fields and constructors are omitted in this sample

        public string GetName()
        {
            return this.name;
        }

        public void SetName(string name)
        {
            this.name = name;
        }
    }
}

In this example:

  • The Student class has a private field name and two methods: GetName (accessor) and SetName (mutator).
  • The accessor method GetName retrieves the value of the private field name.
  • The mutator method SetName sets the value of the private field name.

Using accessor and mutator methods helps in achieving encapsulation by providing controlled access to the internal state of the object. It allows the class to enforce rules or validations when getting or setting the values of its fields.

Behaviors (Methods)

The behaviors of an object is defined by the methods it possesses. A method represents an action or operation that an object can perform. These methods are essential for encapsulating functionality within a class and enabling objects to interact with one another.

Special Methods

ToString()

The ToString method is a method that is defined in the base class System.Object and is overridden by many other classes, allowing them to provide a string representation of their instances. The primary purpose of the ToString method is to return a human-readable string representation of an object.

Default Implementation

The default implementation of the ToString method in the System.Object class returns a string that represents the fully qualified name of the object's type.

C#
Student student = new Student("Kenny Omega", 1234);
string result = student.ToString();

Console.WriteLine(result); 
// Output: "ADEV.Education.Student"

Custom Implementation

Classes can provide their own implementation of the ToString method to return a string that makes more sense in the context of the class. This is particularly useful when you want to represent the object's state or key properties in a readable format.

C#
namespace ADEV.Education
{
    public class Student
    {
        // other class members omitted for this example

        // Custom implementation of ToString method
        public override string ToString()
        {
            return $"{this.name} - {this.identificationNumber}";
        }
    }
}
C#
class Program
{
    static void Main()
    {
        Student student = new Student("Kenny Omega", 1234);
        string result = student.ToString();

        Console.WriteLine(result); 
        // Output: "Kenny Omega - 1234"
    }
}

Implicit Use

The ToString method is implicitly called in various scenarios, such as when you concatenate an object with a string using the + operator or when using Console.WriteLine.

C#
Student student = new Student("Kenny Omega", 1234);

// Implicit use of ToString when concatenating with a string
string message = "Student: " + student;

Console.WriteLine(message);
// Output: "Student: Kenny Omega"

// Implicit use of ToString in Console.WriteLine
Console.WriteLine(student); 
// Output: "Kenny Omega"

this Keyword

The keyword this is used to refer to the current instance of a class. It is particularly useful in scenarios where there might be a naming conflict between a parameter or a local variable and an instance variable (field) within the same class or method. The this keyword helps to disambiguate and explicitly reference the instance variable.

Differentiating Between Instance Variables and Parameters

Consider a scenario where a class has an instance variable (field) and a parameter with the same name. In such cases, using this helps to distinguish between the instance variable and the parameter.

C#
public class Student
{
    private string name;
    private int identificationNumber;

    // Constructor with a parameter having the same name as the instance variable
    public Student(string name, int identificationNumber)
    {
        // Using "this" to refer to the instance variable
        this.name = name;
        this.identificationNumber = identificationNumber;
    }
}

In the constructor above, this.name refers to the instance variable, and name (without this) refers to the parameter.

Overloading Methods

Method overloading in C# allows you to define multiple methods with the same name within the same class, but with different parameter lists. This enables you to provide different ways to call a method, often to perform similar operations on different types or numbers of parameters. The compiler determines which version of the method to call based on the number and types of arguments passed during a method invocation.

C#
public class Calculator
{
    // Method with two integer parameters
    public int Add(int a, int b)
    {
        return a + b;
    }

    // Overloaded method with three integer parameters
    public int Add(int a, int b, int c)
    {
        return a + b + c;
    }

    // Overloaded method with two double parameters
    public double Add(double a, double b)
    {
        return a + b;
    }
}

In this example, the Add method is overloaded three times:

  1. The first version takes two integers.
  2. The second version takes three integers.
  3. The third version takes two doubles.

Overloading Rules:

  1. Parameter Types: Methods can be overloaded based on differences in the number, order, and types of parameters. The return type alone is not sufficient for overloading.

  2. Number of Parameters: Methods must have a different number of parameters, or parameters of different types, to be considered overloaded.

  3. Return Type: The return type alone does not differentiate between overloaded methods.

Overloading Constructors

Constructor overloading in C# allows a class to define multiple constructors with different parameter lists. This enables the creation of objects using various initialization options. Overloaded constructors provide flexibility in how objects are instantiated, allowing developers to choose the appropriate constructor based on the context or specific requirements.

C#
public class Student
{
    private string name;
    private int identificationNumber;

    public Student() 
        : this("")
    {
        // Invoke Student(string)
    }

    public Student(string name) 
        : this(name, 99999)
    {
        // Invokes Student(string, int)
    }

    public Student(string name, int identificationNumber)
    {
        this.name = name;
        this.identificationNumber = identificationNumber;
    }
}

In this example:

  1. The default constructor initializes the object with default values.
  2. The second constructor initializes the object with a name.
  3. The third constructor initializes the object with a name and identification number.
  4. this() invokes another constructor in this class and is used to avoid duplicating statements.

Using Overloaded Constructors:

C#
// Default constructor
Student student = new Student();

// Second constructor
Student student = new Student("Kenny Omega");

// Third constructor
Student student = new Student("Kenny Omega", 1234);   

By providing multiple constructors, you can create instances of the Person class using different sets of parameters, making it more convenient for users of the class to initialize objects in various ways.

Class Documentation

Documentation is required for each class and all of the class' members (except for private fields). Check out a sample of how a class is documented.

Further Reading