Mastering OOPs Concepts in C#: A Comprehensive Guide

Mastering OOPs Concepts in C#: A Comprehensive Guide

15 Oct 2024
Intermediate
904 Views
43 min read
Learn with an interactive course and practical hands-on labs

Free C# Foundation Course: Learn C# In 21 Days

OOPs Concepts in C# with Example

Oops, Concepts in C# is a method for structuring software around objects rather than actions. C#, a powerful and versatile programming language, adopts OOP concepts to assist developers in creating well-organized and reusable code. The four pillars of OOP—encapsulation, inheritance, polymorphism, and abstraction—are critical for creating manageable and efficient applications.

In this OOPs Tutorialwe are going to explain all the concepts of object-oriented programming, including Encapsulation, Class and object, inheritance, and Polymorphism. Let's first see what the OOPs concept is exactly.

What is object-oriented programming?

Object-oriented programming (OOP) is a programming approach that organizes software design around data, rather than functions and logic. An object is a data field that has distinct attributes and behavior.

What is object-oriented programming?

The 4 Pillars of OOP in C# With Example

  • Encapsulation
  • Inheritance
  • Abstraction
  • Polymorphism

1. Encapsulation in C#

  • Encapsulation in C# means that data and the functions that operate on that data are combined into a single unit (class/object).
  • This improves data security, modularity, & reuse.
  • Encapsulation is similar to a School bag where you can store your pen, book, and other items. This refers to the property of encapsulating members and functions.

Syntax

 class SchoolBag {
    book;
    pen;
    ReadBook();
}
  • Encapsulation refers to hiding an object's internal details, or how it performs a function.
  • Encapsulation prevents clients from viewing the inside view, which contains the implementation of the abstraction's behavior.
  • Encapsulation is a technique for protecting information in one object from another.
  • Hide the data for security purposes, such as making the variables private and exposing the property to allow access to the private data that will be public.
  • So, when you access the property, you can both validate and set the data.

encapsulation in C#

Example

 public class Student {
    private string name;
    private int age;

    public string GetName() {
        return name;
    }
    public void SetName(string newName) {
        name = newName;
    }
}

Explanation

  • In the preceding example, the name field is concealed from direct access and can only be edited through methods, hence maintaining data integrity.

Real-World Example of Encapsulation in C#

Let's talk about real-world examples of Encapsulation in C#. Encapsulation can be used to simulate a financial system in which sensitive information, such as account balance, should not be accessed or modified directly. Instead, methods like Deposit and Withdrawal are utilized to interact with the balance while maintaining data integrity and validity.

Encapsulation Example: Bank Account System

 using System;
public class BankAccount {
    // Private field to store the balance
    private double balance;

    // Constructor to initialize the balance
    public BankAccount(double initialBalance) {
        if(initialBalance >= 0) {
            balance = initialBalance;
        } else {
            balance = 0;
            Console.WriteLine("Initial balance can't be negative. Setting balance to 0.");
        }
    }

    // Public method to get the current balance (read-only access)
    public double GetBalance() {
        return balance;
    }

    // Public method to deposit money (with validation)
    public void Deposit(double amount) {
        if(amount > 0) {
            balance += amount;
            Console.WriteLine($"Deposited: {amount}. New Balance: {balance}");
        } else {
            Console.WriteLine("Deposit amount must be positive.");
        }
    }

    // Public method to withdraw money (with validation)
    public void Withdraw(double amount) {
        if(amount > 0 && amount <= balance) {
            balance -= amount;
            Console.WriteLine($"Withdrawn: {amount}. New Balance: {balance}");
        } else {
            Console.WriteLine("Invalid withdrawal amount.");
        }
    }
}

public class Program {
    public static void Main() {
        // Creating a new bank account with an initial balance
        BankAccount account = new BankAccount(500);

        // Checking initial balance
        Console.WriteLine("Initial Balance: " + account.GetBalance());

        // Trying to deposit money
        account.Deposit(200);

        // Trying to withdraw money
        account.Withdraw(150);

        // Trying to withdraw an invalid amount
        account.Withdraw(600);
        
        // Trying to deposit a negative amount
        account.Deposit(-50);
    }
}

Output

 Initial Balance: 500
Deposited: 200. New Balance: 700
Withdrawn: 150. New Balance: 550
Invalid withdrawal amount.
Deposit amount must be positive.

Explanation

  • Encapsulation: The balance field is protected and cannot be accessed directly. Instead, techniques such as GetBalance, Deposit, and Withdraw provide for regulated access.
  • Validation: The Deposit and Withdraw procedures provide logic to validate inputs (for example, forbidding negative deposits or withdrawals that exceed the sum).
  • Output: The code output demonstrates how deposits and withdrawals are processed, as well as how the system handles invalid inputs.
  • 2. Inheritance in C#

    In object-oriented programming (OOP), inheritance is a fundamental concept that allows one class to inherit the properties and methods of another. In C#, inheritance allows you to create a new class (called a derived class or child class) based on an existing class (called a base class or parent class), which promotes code reuse, improves maintainability, and allows for a hierarchical class structure.

    Key Features of Inheritance in C#

    • The base class is the one from which the inheritance is made.
    • Derived Class: A class that derives from the base class.
    • Single Inheritance: C# supports single inheritance, meaning a class can inherit from only one base class.
    • Method Overriding: Derived classes can provide specific implementations for methods that are declared in the base class using the override keyword.

    Syntax of Inheritance in C#

    To declare a derived class in C#, you need a colon (:) followed by the base class name. Here's the syntax
     public class BaseClass {
        // Base class members (fields, methods, etc.)
    }
    public class DerivedClass : BaseClass {
        // Derived class members (fields, methods, etc.)
    }
    

    Example of Inheritance in C#

     using System;
    public class Animal {
        public void Eat() {
            Console.WriteLine("This animal is eating.");
        }
    }
    public class Dog : Animal {
        public void Bark() {
            Console.WriteLine("The dog is barking.");
        }
    }
    public class Program {
        public static void Main() {
            Dog myDog = new Dog();
            // Calling method from the derived class
            myDog.Bark();
            
            // Calling inherited method from the base class
            myDog.Eat();
        }
    }
    

    Output

     The dog is barking.
    This animal is eating.
    

    Explanation

    • Base Class: The base class, Animal, defines the function Eat().
    • Derived Class: Dog is a derived class that derives from Animal. It introduces a new function called Bark().
    • Code Reusability: The Dog class reuses the Animal class's Eat() method, demonstrating how inheritance reduces redundancy.

    Types of Inheritance in C#

    In C#, there are several types of inheritance:
    1. Single Inheritance: A derived class inherits from a single base class.
    2. Multilevel Inheritance: A derived class inherits from a base class, and another class may inherit from the derived class.
    3. Hierarchical Inheritance: Multiple derived classes inherit from a single base class.
    Read More: Types of Inheritance

    Real-World Example of Inheritance in C#

    In a real-world context, inheritance can be used to create a hierarchy of things with similar features. Consider a system that manages many types of employees in a firm. Employees have some basic characteristics, such as a name and an ID, but various sorts of employees, such as full-time and part-time workers, have distinct details and behaviors.

    Example: Employee Management System.

    Let us begin by creating a basic class called Employee, from which we will derive two classes: FullTimeEmployee and PartTimeEmployee. Each derived class will include its own unique features and methods.
     using System;
    public class Employee {
        public string Name { get; set; }
        public int EmployeeID { get; set; }
    
        public Employee(string name, int employeeID) {
            Name = name;
            EmployeeID = employeeID;
        }
    
        // Base class method
        public virtual void DisplayDetails() {
            Console.WriteLine($"Name: {Name}, Employee ID: {EmployeeID}");
        }
    }
    // Derived class representing full-time employees
    public class FullTimeEmployee : Employee {
        public double AnnualSalary { get; set; }
    
        public FullTimeEmployee(string name, int employeeID, double annualSalary)
            : base(name, employeeID) {
            AnnualSalary = annualSalary;
        }
    
        // Overriding the base class method
        public override void DisplayDetails() {
            base.DisplayDetails();
            Console.WriteLine($"Annual Salary: {AnnualSalary}");
        }
    }
    // Derived class representing part-time employees
    public class PartTimeEmployee : Employee {
        public double HourlyWage { get; set; }
        public int HoursWorked { get; set; }
    
        public PartTimeEmployee(string name, int employeeID, double hourlyWage, int hoursWorked)
            : base(name, employeeID) {
            HourlyWage = hourlyWage;
            HoursWorked = hoursWorked;
        }
    
        // Overriding the base class method
        public override void DisplayDetails() {
            base.DisplayDetails();
            Console.WriteLine($"Hourly Wage: {HourlyWage}, Hours Worked: {HoursWorked}, Total Pay: {HourlyWage * HoursWorked}");
        }
    }
    public class Program {
        public static void Main() {
            // Creating a full-time employee object
            FullTimeEmployee ftEmployee = new FullTimeEmployee("John Doe", 101, 60000);
            Console.WriteLine("Full-Time Employee Details:");
            ftEmployee.DisplayDetails();
    
            Console.WriteLine();
    
            // Creating a part-time employee object
            PartTimeEmployee ptEmployee = new PartTimeEmployee("Jane Smith", 102, 20, 25);
            Console.WriteLine("Part-Time Employee Details:");
            ptEmployee.DisplayDetails();
        }
    }
    

    Output

     Full-Time Employee Details:
    Name: John Doe, Employee ID: 101
    Annual Salary: 60000
    
    Part-Time Employee Details:
    Name: Jane Smith, Employee ID: 102
    Hourly Wage: 20, Hours Worked: 25, Total Pay: 500
    

    Explanation

    Employee is the base class that defines common characteristics and methods for all employees, such as Name and EmployeeID. It also contains a method DisplayDetails() that prints the employee information. Derived Classes: Full-Time Employee and Part-Time Employee. FullTimeEmployee creates a new field AnnualSalary and overrides the DisplayDetails() method to incorporate salary data. PartTimeEmployeeadds the HourlyWage and HoursWorked fields and modifies DisplayDetails() to display wage and total pay. Polymorphism: The DisplayDetails() method is overridden in both derived classes, demonstrating how the method in the base class can be tailored to different employee kinds.

    3. Data Abstraction in C#

    Abstraction is a major concept in Object-Oriented Programming (OOP), which focuses on hiding an implementation's underlying details while presenting only the necessary functionalities. Simply put, abstraction enables you to work with objects at a high level, utilizing a clear and easy interface, without having to comprehend how the underlying actions are performed.

    • Abstract classes
    • Interfaces

    Both abstract classes and interfaces allow you to create methods that must be carried out by derived or implementing classes, but they cannot be instantiated directly.

    1. Abstract Classes in C#

    An abstract class in C# serves as a foundation for subsequent classes. It may include both abstract methods (without implementation) and concrete methods (with implementation). Any class that inherits from an abstract class must implement all of its abstract methods.

    Syntax:

    To define an abstract class and its methods, use the "abstract" keyword.
     public abstract class Animal {
        public abstract void MakeSound();  // Abstract method
        public void Eat() {                // Concrete method
            Console.WriteLine("This animal is eating.");
        }
    }
    

    Example of Abstract Class:

     using System;
    public abstract class Animal {
        // Abstract method (no implementation)
        public abstract void MakeSound();
    
        // Concrete method (has implementation)
        public void Sleep() {
            Console.WriteLine("This animal is sleeping.");
        }
    }
    // Derived class providing implementation for abstract method
    public class Dog : Animal {
        public override void MakeSound() {
            Console.WriteLine("The dog barks.");
        }
    }
    public class Program {
        public static void Main() {
            // Animal myAnimal = new Animal(); // Error! Cannot instantiate abstract class
    
            Dog myDog = new Dog();  // Create a Dog object
            myDog.MakeSound();      // Calls the implemented method
            myDog.Sleep();          // Calls the inherited concrete method
        }
    }
    

    Output

     The dog barks.
    This animal is sleeping.
    

    Explanation

    "Animal" is an abstract class, which means you cannot make an instance of it directly. It has an abstract method named MakeSound() that any derived class must implement. The Dog class derives from Animal and has its own implementation of MakeSound(). "Dog" inherits and uses the Sleep() method from Animal, which is a concrete method.

    2. Interfaces in C#

    An interface in C# defines the contract to which a class must adhere. All methods in an interface are abstract by default (that is, they have no implementation), and any class that implements the interface must supply implementations for all of the interface's methods.

    Interface Syntax:

    Declare an interface using the interface keyword.
     public interface IAnimal {
        void MakeSound();  // Interface method (no implementation)
    }
    

    Example of Interface:

     using System;
    public interface IAnimal {
        // Abstract method (implicitly abstract, no implementation)
        void MakeSound();
    }
    
    public class Dog : IAnimal {
        // Implementing the interface method
        public void MakeSound() {
            Console.WriteLine("The dog barks.");
        }
    }
    
    public class Program {
        public static void Main() {
            Dog myDog = new Dog();
            myDog.MakeSound();
        }
    }
    

    Output

     The dog barks.
    

    3. Real-World Example of Abstraction in C#

    Consider a payment system in which several payment methods (credit card, bank transfer, etc.) are used. An abstract class or interface can be used to create a generalized blueprint for these payment mechanisms.

    Example

     using System;
    public interface IPayment {
        void ProcessPayment(double amount);  // Abstract method
    }
    public class CreditCardPayment : IPayment {
        public void ProcessPayment(double amount) {
            Console.WriteLine($"Processing credit card payment of {amount}.");
        }
    }
    public class BankTransferPayment : IPayment {
        public void ProcessPayment(double amount) {
            Console.WriteLine($"Processing bank transfer payment of {amount}.");
        }
    }
    public class Program {
        public static void Main() {
            IPayment payment;
    
            // Process credit card payment
            payment = new CreditCardPayment();
            payment.ProcessPayment(250.00);
    
            // Process bank transfer payment
            payment = new BankTransferPayment();
            payment.ProcessPayment(1500.00);
        }
    }
    

    Output

     Processing credit card payment of 250.
    Processing bank transfer payment of 1500.
    

    Polymorphism in C#

    Polymorphism is an important concept in Object-Oriented Programming (OOP) that allows objects to be viewed as instances of their base class or interface, even if their actual implementation varies. In C#, polymorphism allows a single method to have many implementations based on the object that calls it. This increases the flexibility, extensibility, and reusability of programs.
    There are two types of polymorphism in C#:
    1. Compile-time Polymorphism (also known as static polymorphism)
    2. Run-time Polymorphism (also known as dynamic polymorphism)

    1. Compile time Polymorphism (method overloading)

    Compile-time polymorphism happens when many methods in the same class have the same name but have different numbers or types of parameters. This is referred to as method overloading. The appropriate method to invoke is determined at build time.

    Example of compile-time polymorphism

     using System;
    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 double parameters
        public double Add(double a, double b) {
            return a + b;
        }
    }
    
    public class Program {
        public static void Main() {
            Calculator calc = new Calculator();
            
            // Calling overloaded methods
            Console.WriteLine(calc.Add(2, 3));            // Calls Add(int, int)
            Console.WriteLine(calc.Add(2, 3, 4));         // Calls Add(int, int, int)
            Console.WriteLine(calc.Add(2.5, 3.5));        // Calls Add(double, double)
        }
    }
    

    Output

     5
    9
    6
    

    2. Run-time polymorphism (Method Overriding)

    Method overriding allows for runtime polymorphism. This happens when a derived class adds its own implementation of a method that is already declared in the base class. The method in the base class must be designated as virtual, and the derived class employs the override keyword. This sort of polymorphism allows you to call methods using a base class reference, but the actual function is selected at runtime based on the object's type.

    Example of run-time polymorphism

     using System;
    public class Animal {
        // Virtual method in base class
        public virtual void MakeSound() {
            Console.WriteLine("Animal makes a sound.");
        }
    }
    
    public class Dog : Animal {
        // Overriding the MakeSound method in the derived class
        public override void MakeSound() {
            Console.WriteLine("Dog barks.");
        }
    }
    
    public class Cat : Animal {
        // Overriding the MakeSound method in the derived class
        public override void MakeSound() {
            Console.WriteLine("Cat meows.");
        }
    }
    
    public class Program {
        public static void Main() {
            // Base class reference pointing to a derived class object
            Animal myAnimal;
    
            // Creating a Dog object
            myAnimal = new Dog();
            myAnimal.MakeSound();  // Calls Dog's MakeSound method
    
            // Creating a Cat object
            myAnimal = new Cat();
            myAnimal.MakeSound();  // Calls Cat's MakeSound method
        }
    }
    

    Output

     Dog barks.
    Cat meows.
    

    Explanation

    The base class Animal includes a virtual function MakeSound(), which can be overridden by derived classes. Derived Classes Dog and Cat: Both classes override the MakeSound() function to offer their own implementation. Polymorphic Behavior: In the Main method, the myAnimal reference (of type Animal) is utilized to hold Dog and Cat objects respectively. The actual method called is determined by the object's actual type (Dog or Cat), not its reference type (Animal).

    Read More: Method Overloading and Method Overriding in C#

    Real-World Example of Polymorphism in C#

    Consider a simple payment processing system that accepts payments via several ways, such as credit card or PayPal. Each payment method has its own means of processing payments, but they all follow the same contract (interface or base class).

    Example

     using System;
    public abstract class Payment {
        // Abstract method to be implemented by derived classes
        public abstract void ProcessPayment(double amount);
    }
    
    public class CreditCardPayment : Payment {
        // Overriding the abstract method
        public override void ProcessPayment(double amount) {
            Console.WriteLine($"Processing credit card payment of {amount}.");
        }
    }
    public class PayPalPayment : Payment {
        // Overriding the abstract method
        public override void ProcessPayment(double amount) {
            Console.WriteLine($"Processing PayPal payment of {amount}.");
        }
    }
    public class Program {
        public static void Main() {
            Payment payment;
    
            // Processing a credit card payment
            payment = new CreditCardPayment();
            payment.ProcessPayment(100.00);
    
            // Processing a PayPal payment
            payment = new PayPalPayment();
            payment.ProcessPayment(200.00);
        }
    }
    

    Output

     Processing credit card payment of 100.
    Processing PayPal payment of 200.
    

    Additional OOP Concepts in C#

    1. Classes & Objects

    Classes are blueprints for constructing objects that are instances of the class. Objects contain data in fields (variables) and expose behavior via methods.
     public class Person {
        public string Name;
        public int Age;
    }
    Person person1 = new Person();
    person1.Name = "John";
    person1.Age = 25;
    

    2. Constructor

    Constructors in Csharp are special methods that are executed when an object is created. They initialize the object's state.
     public class Person {
        public string Name;
        public int Age;
    
        public Person(string name, int age) {
            Name = name;
            Age = age;
        }
    }
    

    3. Interfaces

    An interface in C# specifies the contract that a class must obey. It describes methods and attributes but not their implementations.
     public interface IAnimal {
        void MakeSound();
    }
    
    public class Dog : IAnimal {
        public void MakeSound() {
            Console.WriteLine("Bark");
        }
    }
    

    4. Access modifiers

    Access modifiers control the visibility of classes and their members.
    C#'s common access modifiers are:
    • Public: accessible from anyplace.
    • Private: Accessible only within the class.
    • Protected: accessible within the class and its descendant classes.
    • Internal: accessible within the same assembly.

    5. Static vs Instance Members

    • Instance Members: These members belong to a specific instance of the class and may only be accessed by constructing a class object.
    • Static Members: Belong to the class itself and can be accessed without generating a new instance of the class.
     public class Calculator {
        public static int Add(int a, int b) {
            return a + b;
        }
    }
    int result = Calculator.Add(3, 4);

    6. Properties in C# 

    Properties in C# are a versatile way to read, write, or compute the value of a private field, replacing getter and setter methods.
     public class Person {
        private string name;
    
        public string Name {
            get { return name; }
            set { name = value; }
        }
    }
    

    Real-World Example: Implementing OOP Concepts in C#

     public class Account {
        private double balance;
        public Account(double initialBalance) {
            balance = initialBalance;
        }
    
        public double Balance {
            get { return balance; }
        }
    
        public void Deposit(double amount) {
            if(amount > 0) {
                balance += amount;
            }
        }
        public void Withdraw(double amount) {
            if(amount > 0 && amount <= balance) {
                balance -= amount;
            }
        }
    }
    public class SavingsAccount : Account {
        private double interestRate;
    
        public SavingsAccount(double initialBalance, double rate) : base(initialBalance) {
            interestRate = rate;
        }
    
        public void AddInterest() {
            Deposit(Balance * interestRate);
        }
    }
    

    Explanation

    The banking system includes Encapsulation: The balance is secret, with access supplied by means. Inheritance: SavingsAccount inherits from Account. Polymorphism: The Deposit and Withdraw methods can be customized for other account types if necessary.

    Advantages of Using OOP in C#

    1. Code Reusability: OOP encourages code reuse through inheritance, which reduces redundancy and increases maintainability.
    2. Modularity: Large programs can be broken into smaller, more manageable classes, improving clarity and making debugging easier. 
    3. Scalability: OOP simplifies scaling apps. New features can be added with few changes to the existing code. 
    4. Abstraction and Flexibility: By hiding implementation details and exposing only the essential interfaces, OOP decreases complexity while increasing flexibility.
    5. Ease of Maintenance: OOP's modular approach enables teams to work on multiple areas of an application simultaneously, making it easier to maintain and upgrade.
    Conclusion
    Object-Oriented Programming in C# provides a solid foundation for developing sophisticated software systems. OOP, which is built on the principles of encapsulation, abstraction, inheritance, and polymorphism, gives developers the tools they need to produce clean, efficient, and maintainable code. C# extends OOP ideas with concepts such as access modifiers, interfaces, and properties, allowing developers to create scalable and adaptable programs. If you are preparing for an exam or interview regarding OOPS concepts, then consider our OOPs Interview Questions and Answers in C#.

    FAQs

    OOP can make software development more modular, reusable, and maintainable, which can make it easier to upgrade and update the system.

    Object-oriented programming (OOP) is a programming paradigm that uses "objects" and their interactions to design applications and computer programs.

    Using the OOPs methodology, one can enhance the code reusability and save development time. 

    Take our Csharp skill challenge to evaluate yourself!

    In less than 5 minutes, with our skill challenge, you can identify your knowledge gaps and strengths in a given skill.

    GET FREE CHALLENGE

    Share Article
    About Author
    Shailendra Chauhan (Microsoft MVP, Founder & CEO at Scholarhat by DotNetTricks)

    Shailendra Chauhan, Founder and CEO of ScholarHat by DotNetTricks, is a renowned expert in System Design, Software Architecture, Azure Cloud, .NET, Angular, React, Node.js, Microservices, DevOps, and Cross-Platform Mobile App Development. His skill set extends into emerging fields like Data Science, Python, Azure AI/ML, and Generative AI, making him a well-rounded expert who bridges traditional development frameworks with cutting-edge advancements. Recognized as a Microsoft Most Valuable Professional (MVP) for an impressive 9 consecutive years (2016–2024), he has consistently demonstrated excellence in delivering impactful solutions and inspiring learners.

    Shailendra’s unique, hands-on training programs and bestselling books have empowered thousands of professionals to excel in their careers and crack tough interviews. A visionary leader, he continues to revolutionize technology education with his innovative approach.
    Accept cookies & close this