C# Part 9: Object-oriented Programming In C# (OOP Pillars)

C# Part 9: Object-oriented Programming In C# (OOP Pillars)


Please Subscribe Youtube| Like Facebook | Follow Twitter

Object-Oriented Programming (OOP Pillars) In C#

In this article, we will provide a detailed overview of Object-Oriented Programming In C# (OOP Pillars) and provide examples of how they are used in C# programming.

The Pillars of Object-Oriented Programming

OOP is built upon four key pillars: encapsulation, inheritance, polymorphism, and abstraction. In this article we explore them as in previous article we explored classes and objects.

Encapsulation

Encapsulation in C# is a fundamental principle of object-oriented programming that involves bundling data and methods together within a class. The purpose of encapsulation is to hide the internal details of an object and provide a controlled way to access and modify its state.

In C#, encapsulation is achieved by using access modifiers to specify the visibility and accessibility of class members. The access modifiers available in C# are:

  • Public: Members declared as public can be accessed from anywhere, both within the class and from other classes.
  • Private: Members declared as private are only accessible within the same class. They cannot be accessed from any other class.
  • Protected: Members declared as protected are accessible within the same class and its subclasses (derived classes). They are not accessible from outside the class hierarchy.
  • Internal: Members declared as internal are accessible within the same assembly (a compiled unit of code, such as a DLL or EXE). They are not accessible from outside the assembly.
  • Protected Internal: Members declared as protected internal are accessible within the same assembly and its subclasses. They can also be accessed from outside the assembly by classes that inherit from the declaring class.

Here’s an example that demonstrates encapsulation in C#:

using System;
public class EncapsulationExample
{
    private int privateData;

    public void SetData(int data)
    {
        // Perform any necessary validation or logic
        privateData = data;
    }

    public int GetData()
    {
        return privateData;
    }
}

public class Program
{
    public static void Main()
    {
        EncapsulationExample example = new EncapsulationExample();
        example.SetData(42);
        int data = example.GetData();
        Console.WriteLine("Data: " + data);
    }
}

Output

Data: 42

In this example, the privateData field is encapsulated within the EncapsulationExample class using the private access modifier. The SetData method allows setting the value of privateData with any necessary validation or logic. The GetData method returns the value of privateData. The Main method demonstrates the usage of the encapsulated data by creating an instance of EncapsulationExample, setting the data using the SetData method, and retrieving the data using the GetData method.

Inheritance

In C#, inheritance is also a fundamental concept in object-oriented programming that allows classes to inherit properties and behaviors from other classes. It promotes code reuse, modularity, and extensibility. In C#, inheritance is achieved using the “:” symbol followed by the name of the base class.

base keyword

The base keyword is used to refer to the base class (parent class) from within a derived class (subclass). It allows you to access the base class’s members, invoke the base class’s constructor, or differentiate between base class and derived class members with the same name.

using System;
public class Animal
{
    public string Name { get; set; }

    public Animal(string name)
    {
        Name = name;
    }

    public void Eat()
    {
        Console.WriteLine("The animal is eating.");
    }
}

public class Dog : Animal
{
    public Dog(string name) : base(name)
    {
    }

    public void Bark()
    {
        Console.WriteLine("The dog is barking.");
    }

    public void DisplayAnimalName()
    {
        Console.WriteLine("Animal name: " + base.Name); // Accessing base class's Name property
    }
}

public class Program
{
    public static void Main()
    {
        Dog dog = new Dog("Buddy");
        dog.Eat(); // Invoking base class's Eat method
        dog.Bark();
        dog.DisplayAnimalName(); // Invoking base class's Name property
    }
}

Output

The animal is eating.
The dog is barking.
Animal name: Buddy

The code defines two classes: Animal and Dog.

The Animal class has a property called Name, a constructor that takes a name parameter to initialize the Name property, and a method named Eat which outputs a message indicating that the animal is eating.

The Dog class is a subclass of Animal. It has a constructor that takes a name parameter and passes it to the base class constructor using the base keyword. The Dog class also has a method named Bark that outputs a message indicating that the dog is barking, and a method named DisplayAnimalName which uses the base keyword to access the Name property of the base class and displays it.

In the Main method, an instance of the Dog class is created with the name “Buddy”. The Eat method inherited from the Animal class is invoked on the dog object, followed by the Bark method defined in the Dog class. Finally, the DisplayAnimalName method is called to display the name of the animal.

This demonstrates how the Dog class inherits the properties and methods from the Animal class, and how the base keyword is used to access the base class members within the derived class.This demonstrates how the Dog class inherits the properties and methods from the Animal class, and how the base keyword is used to access the base class members within the derived class.

Polymorphism

Polymorphism in C# is a fundamental concept in object-oriented programming that allows objects of different classes to be treated as objects of a common base class or interface. It provides flexibility and extensibility by enabling objects to be used interchangeably and exhibit different behaviors based on their actual type at runtime.

In C#, there are two main types of polymorphism

Method Overloading (Compile-time Polymorphism)

Method overloading in C# allows a class to have multiple methods with the same name but different parameters. This enables developers to define several methods with different input parameters but the same method name, providing flexibility and code reusability. The appropriate method to invoke is determined by the compiler at compile-time based on the method name and the arguments passed.

Method Overriding (Runtime Polymorphism)

Method overriding occurs when a derived class provides its own implementation of a method that is already defined in its base class. The derived class method must have the same name, return type, and parameters as the method in the base class. This allows the derived class to provide specialized behavior while maintaining the same method signature. The appropriate method to invoke is determined at runtime based on the actual type of the object.

In C#, the method signature is determined by the combination of the method name and the parameter list. It specifies the unique identifier of a method and helps distinguish it from other methods in the same class. The method signature does not include the return type, access modifiers, or exceptions.

Example

using System;
public class PolymorphismExample {
    public static void Main() {
        Shape shape1 = new Circle();
        Shape shape2 = new Square();
        shape1.Draw(); // Output: Drawing a circle. (Method Overriding)
        shape1.Draw("Red"); // Output: Drawing a shape with color: Red (Method Overloading)
        shape2.Draw(); // Output: Drawing a square. (Method Overriding)
        shape2.Draw("Blue"); // Output: Drawing a shape with color: Blue (Method Overloading)
        Circle circle = new Circle();
        circle.Draw(5); // Output: Drawing a circle with radius: 5 (Method Overloading)
        Square square = new Square();
        square.Draw(4.5); // Output: Drawing a square with side: 4.5 (Method Overloading)
    }
}

class Shape {
    // Method Overriding
    public virtual void Draw() {
        Console.WriteLine("Drawing a shape.");
    }
    
    // Method Overloading
    public void Draw(string color) {
        Console.WriteLine("Drawing a shape with color: " + color);
    }
}

class Circle : Shape {
    // Method Overriding
    public override void Draw() {
        Console.WriteLine("Drawing a circle.");
    }
    
    // Method Overloading
    public void Draw(int radius) {
        Console.WriteLine("Drawing a circle with radius: " + radius);
    }
}

class Square : Shape {
    // Method Overriding
    public override void Draw() {
        Console.WriteLine("Drawing a square.");
    }
    
    // Method Overloading
    public void Draw(double side) {
        Console.WriteLine("Drawing a square with side: " + side);
    }
}

Output

Drawing a circle.
Drawing a shape with color: Red
Drawing a square.
Drawing a shape with color: Blue
Drawing a circle with radius: 5
Drawing a square with side: 4.5

In this example, the PolymorphismExample class contains the Main method where polymorphism is demonstrated. The Shape class serves as the base class, and the Circle and Square classes are derived classes that inherit from Shape.

Method Overriding

The Draw method in the Shape class is marked with the virtual keyword to indicate that it can be overridden by derived classes. The Circle and Square classes override this method to provide their own specific implementation.
When shape1 is assigned a Circle object and shape2 is assigned a Square object, calling the Draw method on these objects invokes the overridden method in the respective derived classes, resulting in the output “Drawing a circle” and “Drawing a square” respectively.

Method Overloading

The Draw method in the Shape class is overloaded with a different parameter list. The method can accept a string parameter to specify the color of the shape.
The Circle and Square classes also have their own overloaded versions of the Draw method, accepting different parameter types specific to their shape (e.g., int radius for Circle and double side for Square).
Calling the Draw method with different arguments demonstrates method overloading and produces different output based on the provided arguments.

Polymorphism allows objects of different classes (Circle and Square) to be treated as objects of the common base class (Shape). This enables code reuse, flexibility, and extensibility by providing a unified interface to interact with objects of diverse types.

Abstraction

Abstraction in C# is a fundamental concept in object-oriented programming that focuses on hiding unnecessary implementation details and exposing only the essential features and behaviors of an object. It allows us to create abstract classes and interfaces that define a common structure and behavior for a group of related classes without providing the actual implementation.

Abstract Class

An abstract class in C# is a class that cannot be instantiated and can only be used as a superclass for other classes. It may contain both abstract and non-abstract methods. Abstract methods are declared without an implementation and must be overridden by the concrete subclasses that derive from the abstract class. Abstract classes provide a way to define a common interface and behavior while allowing subclasses to provide specific implementations.

Interface

An interface in C# is a collection of abstract methods that define a contract for implementing classes. It is similar to an abstract class but can only contain abstract methods and constants. A class can implement one or more interfaces to inherit their abstract methods. By implementing an interface, a class guarantees that it provides the functionality defined by the interface. Interfaces enable multiple inheritance of behavior and allow classes to be loosely coupled, promoting code modularity and flexibility.

using System;
public class AbstractionExample {
    public static void Main() {
        Vehicle car = new Car("Toyota");
        car.DisplayBrand(); // Output: Brand: Toyota
        car.Start();        // Output: Car is starting.
        Engine engine = new Car("Honda");
        engine.Run();       // Output: Car engine is running.
    }
}

// Abstract class representing a vehicle
abstract class Vehicle {
    private string brand;

    public Vehicle(string brand) {
        this.brand = brand;
    }

    public abstract void Start();   // Abstract method

    public void DisplayBrand() {
        Console.WriteLine("Brand: " + brand);
    }
}

// Interface representing a vehicle engine
interface Engine {
    void Run();
}

// Concrete class representing a car
class Car : Vehicle, Engine {
    public Car(string brand) : base(brand) {
    }

    public override void Start() {
        Console.WriteLine("Car is starting.");
    }

    public void Run() {
        Console.WriteLine("Car engine is running.");
    }
}

Output

Brand: Toyota
Car is starting.
Car engine is running.

In this example, the AbstractionExample class contains the Main method where abstraction is demonstrated. The code showcases the use of abstract classes and interfaces to achieve abstraction in C#.

Abstract Class

The Vehicle class is declared as an abstract class using the abstract keyword. It contains an abstract method Start() and a non-abstract method DisplayBrand().
Abstract methods are declared without an implementation and must be overridden by the concrete subclasses.
The Car class inherits from the Vehicle abstract class and provides an implementation for the Start() method.
The Vehicle class is instantiated with the Car object, showcasing that abstract classes cannot be directly instantiated.
The DisplayBrand() method is invoked on the car object, displaying the brand name.

Interface

The Engine interface represents a vehicle engine and declares the Run() method.
The Car class implements the Engine interface, providing an implementation for the Run() method.
The engine variable is of type Engine and is assigned a Car object, showcasing that interfaces can be used for polymorphism.
The Run() method is invoked on the engine object, demonstrating the usage of the interface.

Differences between an abstract class and an interface in C#

TermAbstract ClassInterface
DefinitionAn abstract class in C# serves as a foundational class that cannot be directly instantiated. It provides a blueprint for derived classes to inherit and defines a common structure and behavior.An interface in C# acts as a contract that outlines a set of methods, properties, and events that implementing classes must adhere to.
InstantiationAbstract classes cannot be instantiated directly in C#.Interfaces cannot be instantiated directly in C#.
InheritanceAbstract classes support single inheritance in C#, allowing a derived class to inherit from only one abstract class.Interfaces support multiple inheritance in C#, enabling a class to implement multiple interfaces.
ImplementationAbstract classes can contain both abstract and non-abstract members, providing the flexibility to define common behavior and partial implementations.Interfaces can only have abstract members, such as methods, properties, and events, ensuring that implementing classes provide complete implementations.
ConstructorsAbstract classes can have constructors in C#, allowing initialization of derived classes.Interfaces cannot have constructors in C#.
Code ReusabilityAbstract classes promote code reusability in C# by allowing derived classes to inherit and extend common implementation, fostering modular and efficient development.Interfaces facilitate code reusability in C# by providing a contract that multiple unrelated classes can implement, promoting flexibility and interoperability.
Default ImplementationAbstract classes in C# can provide default implementations for methods, allowing derived classes to override or use the provided implementation.Interfaces in C# do not offer default implementations, requiring implementing classes to provide their own specific implementation for each member.
Use CasesAbstract classes in C# are suitable for creating base classes with shared attributes and behaviors, offering a foundation for derived classes to specialize and extend functionality.Interfaces in C# are useful for defining contracts and enforcing specific behavior across unrelated classes, enabling polymorphism and facilitating code integration.
RelationshipAbstract classes in C# establish an “is-a” relationship, where derived classes are considered specialized versions of the abstract class.Interfaces in C# establish a “can-do” relationship, allowing classes to declare capabilities without being tied to a specific inheritance hierarchy.

Conclusion

Encapsulation, inheritance, polymorphism, and abstraction are four fundamental concepts in C# object-oriented programming. Together, they provide a powerful and flexible foundation for creating modular, maintainable, and extensible code.

C# Beginner Tutorial Series

Please Subscribe Youtube| Like Facebook | Follow Twitter


Leave a Reply

Your email address will not be published. Required fields are marked *