C# Object Oriented Programming - Inheritance

Inheritance is one of the four core principles of Object-Oriented Programming (OOP), along with Encapsulation, Polymorphism, and Abstraction. In C#, inheritance allows a class to inherit properties and methods from another class, promoting code reuse and establishing a hierarchical relationship between classes.

1. Purpose of Inheritance

The primary purposes of inheritance include:

- Code Reuse: Avoids redundancy by reusing existing code.

- Modularity: Establishes a structure where related classes are grouped hierarchically.

- Maintainability: Simplifies the process of updating and maintaining code.

- Extendability: Allows extending existing functionality without modifying the base class.

2. Terminology

- Base Class (Parent or Superclass): The class whose members (fields, properties, and methods) are inherited.

- Derived Class (Child or Subclass): The class that inherits members from the base class.

- is-a Relationship: The derived class has an "is-a" relationship with the base class. For example, a Car is-a Vehicle.

3. Syntax of Inheritance

In C#, inheritance is achieved using the : symbol. A derived class can inherit from only one base class, as C# does not support multiple inheritance (however, it does support multiple interface implementation).

Syntax
class DerivedClass : BaseClass
{
    // Additional members for the derived class
}

Example
using System;

class Vehicle
{
    public void Start()
    {
        Console.WriteLine("Vehicle starting...");
    }
}

class Car : Vehicle
{
    public void Drive()
    {
        Console.WriteLine("Car driving...");
    }
}

class Program
{
    static void Main()
    {
        Car myCar = new Car();
        myCar.Start();  // Inherited from Vehicle
        myCar.Drive();  // Defined in Car
    }
}

Output:
Vehicle starting...
Car driving...

4. Access Modifiers and Inheritance

The accessibility of members in a base class affects whether they are accessible in a derived class. Here’s how access modifiers work in inheritance:

- public: Accessible from anywhere, including derived classes.

- protected: Accessible within the same class and in derived classes.

- private: Accessible only within the same class, not in derived classes.

- internal: Accessible within the same assembly.

- protected internal: Accessible within the same assembly and from derived classes.

- private protected: Accessible only within the same assembly and from derived classes within that assembly.

Example of protected Access Modifier
using System;

class Animal
{
    protected string species;

    public Animal(string species)
    {
        this.species = species;
    }

    protected void DisplaySpecies()
    {
        Console.WriteLine($"Species: {species}");
    }
}

class Dog : Animal
{
    public Dog() : base("Dog")
    {
    }

    public void ShowSpecies()
    {
        DisplaySpecies(); // Accessible because it's protected
    }
}

class Program
{
    static void Main()
    {
        Dog dog = new Dog();
        dog.ShowSpecies(); // Output: Species: Dog
    }
}

Output:
Species: Dog

5. Constructor Chaining with base Keyword

The base keyword allows the derived class to call a constructor or method in the base class. This is especially useful for initializing base class properties from the derived class.

Example of Constructor Chaining
using System;

class Person
{
    protected string name;

    public Person(string name)
    {
        this.name = name;
    }
}

class Employee : Person
{
    public string EmployeeId { get; }

    public Employee(string name, string employeeId) : base(name)
    {
        EmployeeId = employeeId;
    }

    public void DisplayInfo()
    {
        Console.WriteLine($"Name: {name}, Employee ID: {EmployeeId}");
    }
}

class Program
{
    static void Main()
    {
        Employee employee = new Employee("Alice", "E12345");
        employee.DisplayInfo(); // Output: Name: Alice, Employee ID: E12345
    }
}

Output:
Name: Alice, Employee ID: E12345

6. Method Overriding

Method overriding allows a derived class to provide a specific implementation of a method that is defined in the base class. The method in the base class must be marked as virtual, and the derived class overrides it using the override keyword.

Example of Method Overriding
using System;

class Animal
{
    public virtual void Speak()
    {
        Console.WriteLine("Animal speaks");
    }
}

class Dog : Animal
{
    public override void Speak()
    {
        Console.WriteLine("Dog barks");
    }
}

class Program
{
    static void Main()
    {
        Animal animal = new Animal();
        animal.Speak(); // Output: Animal speaks

        Dog dog = new Dog();
        dog.Speak(); // Output: Dog barks
    }
}

Output:
Animal speaks
Dog barks

7. Sealing a Class or Method

The sealed keyword in C# is used to prevent a class from being inherited or to prevent a method from being overridden.

- Sealing a Class: A sealed class cannot be inherited by other classes.

- Sealing a Method: A method marked as sealed in a derived class prevents further overrides of that method in subclasses.

Example of Sealed Method
using System;

class Vehicle
{
    public virtual void Start()
    {
        Console.WriteLine("Vehicle starting...");
    }
}

class Car : Vehicle
{
    public sealed override void Start()
    {
        Console.WriteLine("Car starting with key...");
    }
}

class Program
{
    static void Main()
    {
        Car car = new Car();
        car.Start(); // Output: Car starting with key...
    }
}

Output:
Car starting with key...

8. Abstract Classes and Methods

An abstract class is a class that cannot be instantiated and is meant to be inherited. It may contain abstract methods (methods without a body) that must be implemented in derived classes.

- Abstract Method: Declared with abstract keyword and no body. Must be overridden in derived classes.

- Abstract Class: Declared with abstract keyword. Cannot be instantiated directly.

Example of Abstract Class and Method
using System;

abstract class Shape
{
    public abstract double CalculateArea();

    public void DisplayShape()
    {
        Console.WriteLine("This is a shape.");
    }
}

class Circle : Shape
{
    private double radius;

    public Circle(double radius)
    {
        this.radius = radius;
    }

    public override double CalculateArea()
    {
        return Math.PI * radius * radius;
    }
}

class Program
{
    static void Main()
    {
        Shape shape = new Circle(5);
        Console.WriteLine("Area of Circle: " + shape.CalculateArea()); // Output: Area of Circle: 78.53981633974483
    }
}

Output:
Area of Circle: 78.53981633974483

9. Casting and Type Checking with Inheritance

When working with inheritance, you may need to check an object’s type or cast it to the appropriate type.

- Type Checking (is operator): Checks if an object is of a specific type.

- Type Casting (as operator): Attempts to cast an object to a specific type.

Example of Type Checking and Casting
using System;

class Animal
{
    public virtual void Speak()
    {
        Console.WriteLine("Animal sound");
    }
}

class Dog : Animal
{
    public override void Speak()
    {
        Console.WriteLine("Dog barks");
    }

    public void Fetch()
    {
        Console.WriteLine("Dog fetches the ball");
    }
}

class Program
{
    static void Main()
    {
        Animal myAnimal = new Dog();

        if (myAnimal is Dog myDog)
        {
            myDog.Speak();  // Output: Dog barks
            myDog.Fetch();  // Output: Dog fetches the ball
        }
    }
}

Output:
Dog barks
Dog fetches the ball

10. Hiding Members with new Keyword

The new keyword can be used to hide a member of the base class when a derived class defines a member with the same name.

Example of Member Hiding
using System;

class Vehicle
{
    public void Start()
    {
        Console.WriteLine("Vehicle starting...");
    }
}

class Car : Vehicle
{
    public new void Start()
    {
        Console.WriteLine("Car starting with ignition key...");
    }
}

class Program
{
    static void Main()
    {
        Vehicle vehicle = new Vehicle();
        vehicle.Start(); // Output: Vehicle starting...

        Car car = new Car();
        car.Start(); // Output: Car starting with ignition key...

        Vehicle myCarAsVehicle = new Car();
        myCarAsVehicle.Start(); // Output: Vehicle starting...
    }
}

Output:
Vehicle starting...
Car starting with ignition key...
Vehicle starting...

11. Summary

Inheritance in C# allows for a structured and hierarchical organization of code, where shared behaviors and properties can be placed in base classes and inherited by derived classes. By understanding how inheritance works, including method overriding, constructor chaining, and abstract classes, developers can build scalable and maintainable applications.

Key points:

- Inheritance facilitates code reuse and modularity.

- Access modifiers control visibility of members in derived classes.

- Method overriding enables polymorphism by providing specific implementations in derived classes.

- Abstract classes define a blueprint for other classes and cannot be instantiated directly.

Effective use of inheritance allows for flexible, reusable, and organized code, helping developers adhere to the principles of Object-Oriented Programming.

Previous: C# Object Oriented Programming - Encapsulation | Next: C# Polymorphism

<
>