Top Exception Handling Examples in Programming

Mahir barotMahir barot
9 min read

Table of contents:-

  • Exception handling Example

  • Code for exception handling

  • Custom exceptions with bankApp example

  • Access modifiers in c# and it's scope.


Scenario: File Reading

We’ll create a program that attempts to read a file. The file may or may not exist, and we will demonstrate how the program behaves with and without exception handling.


Without Exception Handling

using System;
using System.IO;

class Program
{
    static void Main()
    {
        string filePath = "example.txt";

        // Attempt to read the file
        string content = File.ReadAllText(filePath);

        // Print the content to the console
        Console.WriteLine("File content:");
        Console.WriteLine(content);
    }
}

Explanation:

  • If the file example.txt does not exist, this code will throw a FileNotFoundException, causing the application to crash and terminate abruptly.

  • The user will see an unhandled exception error, and there’s no way to gracefully handle or inform them about the issue.


With Exception Handling

using System;
using System.IO;

class Program
{
    static void Main()
    {
        string filePath = "example.txt";

        try
        {
            // Attempt to read the file
            string content = File.ReadAllText(filePath);

            // Print the content to the console
            Console.WriteLine("File content:");
            Console.WriteLine(content);
        }
        catch (FileNotFoundException ex)
        {
            // Handle the specific exception
            Console.WriteLine("Error: The file was not found.");
            Console.WriteLine($"Details: {ex.Message}");
        }
        catch (Exception ex)
        {
            // Handle any other exceptions
            Console.WriteLine("An unexpected error occurred.");
            Console.WriteLine($"Details: {ex.Message}");
        }
        finally
        {
            // Cleanup or final operations
            Console.WriteLine("Execution completed.");
        }
    }
}

Explanation:

  • try Block: Contains the code that may throw an exception.

  • catch Block: Catches specific exceptions (e.g., FileNotFoundException) and provides a meaningful error message to the user.

  • catch (Exception ex) Block: Handles any other unexpected exceptions, ensuring that the program does not crash unexpectedly.

  • finally Block: Executes regardless of whether an exception was thrown or not. It can be used for cleanup operations or to display final messages.

Comparison

Without Exception Handling:

  • The program will crash if the file is not found.

  • Users receive a generic error message from the runtime, which may be confusing.

With Exception Handling:

  • The program handles the error gracefully by catching exceptions.

  • Users receive informative messages, and the program continues to execute or ends cleanly with a message indicating completion.

Benefits of Exception Handling:

  • User-Friendly: Provides clear feedback and prevents abrupt application termination.

  • Resilient: Allows the application to handle errors gracefully and continue running or exit cleanly.

  • Debugging: Helps identify and log errors for further investigation.

By using exception handling, the code becomes more robust and user-friendly, ensuring a smoother experience even when unexpected issues arise.

code for real life example

using System;

// Custom Exception for Insufficient Funds
public class InsufficientFundsException : Exception
{
    public InsufficientFundsException(string message) : base(message) { }
}

// Custom Exception for Invalid Amount
public class InvalidAmountException : Exception
{
    public InvalidAmountException(string message) : base(message) { }
}

public class BankAccount
{
    public decimal Balance { get; private set; }

    public BankAccount(decimal initialBalance)
    {
        if (initialBalance < 0)
            throw new InvalidAmountException("Initial balance cannot be negative.");
        Balance = initialBalance;
    }

    public void Withdraw(decimal amount)
    {
        if (amount <= 0)
            throw new InvalidAmountException("Withdrawal amount must be greater than zero.");

        if (amount > Balance)
            throw new InsufficientFundsException("Insufficient funds for this withdrawal.");

        Balance -= amount;
        Console.WriteLine($"Withdrawal successful. New balance: {Balance:C}");
    }

    public void DisplayBalance()
    {
        Console.WriteLine($"Current balance: {Balance:C}");
    }
}

class Program
{
    static void Main()
    {
        BankAccount account = null;

        try
        {
            // Set initial balance to a valid amount for demonstration
            decimal initialBalance = 1000.00m;

            // Create BankAccount object
            account = new BankAccount(initialBalance);
        }
        catch (InvalidAmountException ex)
        {
            Console.WriteLine("Error: " + ex.Message);
            return; // Exit the application if initialization fails
        }

        bool running = true;

        while (running)
        {
            Console.Clear();
            Console.WriteLine("Bank Account Menu");
            Console.WriteLine("1. Check Balance");
            Console.WriteLine("2. Withdraw Funds");
            Console.WriteLine("3. Exit");
            Console.Write("Please choose an option: ");

            string choice = Console.ReadLine();

            switch (choice)
            {
                case "1":
                    // Check Balance
                    account?.DisplayBalance();
                    break;

                case "2":
                    // Withdraw Funds
                    Console.Write("Enter the amount to withdraw: ");
                    if (decimal.TryParse(Console.ReadLine(), out decimal amount))
                    {
                        try
                        {
                            account?.Withdraw(amount);
                        }
                        catch (InvalidAmountException ex) when (ex.Message.Contains("zero"))
                        {
                            Console.WriteLine("Error: " + ex.Message);
                        }
                        catch (InsufficientFundsException ex) when (ex.Message.Contains("Insufficient"))
                        {
                            Console.WriteLine("Error: " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine("An unexpected error occurred.");
                            Console.WriteLine($"Details: {ex.Message}");
                        }
                        finally
                        {
                            Console.WriteLine("Transaction processing completed.");
                        }
                    }
                    else
                    {
                        Console.WriteLine("Invalid amount entered.");
                    }
                    break;

                case "3":
                    // Exit
                    running = false;
                    Console.WriteLine("Exiting the application. Have a great day!");
                    break;

                default:
                    Console.WriteLine("Invalid choice. Please select a valid option.");
                    break;
            }

            Console.WriteLine("Press Enter to return to the menu...");
            Console.ReadLine();
        }
    }
}

Difference between const and readonly:

Example: ReadOnly Property in a Class

In this example, we'll create a class Person with a read-only property Name:

using System;

public class Person
{
    // Private field to store the name
    private readonly string name;

    // Constructor to initialize the name
    public Person(string name)
    {
        this.name = name;
    }

    // ReadOnly property to access the name
    public string Name
    {
        get { return name; }
    }
}

public class Program
{
    public static void Main()
    {
        // Create a new Person object
        Person person = new Person("Alice");

        // Access the read-only property
        Console.WriteLine("Name: " + person.Name);

        // The following line would produce a compile-time error because Name is read-only
        // person.Name = "Bob"; // Uncommenting this line will cause a compilation error
    }
}

Explanation

  1. Private Field: We declare a private field name in the Person class to store the value of the Name property. This field is marked as readonly, which means it can only be assigned a value during its declaration or in the constructor of the class.

  2. Constructor: The constructor initializes the name field when a new instance of Person is created.

  3. ReadOnly Property: The Name property provides a way to read the value of name but does not provide a way to modify it. Since there is no set accessor, the property is effectively read-only from outside the class.

  4. Usage: In the Main method, we create a new Person object and access the Name property. Trying to modify the Name property would result in a compile-time error because the property does not have a set accessor.

This approach ensures that the Name property is immutable after the Person object is created, which is useful for scenarios where you want to ensure that certain values cannot be changed once set.

Access specifiers in C# with explanation and examples:-

In C#, access specifiers (also known as access modifiers) control the visibility and accessibility of classes, methods, properties, and other members. They help manage the encapsulation and protect the internal state of objects.

Here’s an overview of the main access specifiers in C#, along with examples to illustrate their usage:

1. public

The public access specifier makes members accessible from any other class or assembly. This is the least restrictive access level.

Example:

using System;

public class Car
{
    // Public field
    public string Make;

    // Public method
    public void Start()
    {
        Console.WriteLine("Car started.");
    }
}

public class Program
{
    public static void Main()
    {
        Car myCar = new Car();
        myCar.Make = "Toyota"; // Accessing public field
        myCar.Start();        // Calling public method

        Console.WriteLine("Car make: " + myCar.Make);
    }
}

In this example, both the Make field and the Start method are public, so they can be accessed from anywhere.

2. private

The private access specifier restricts access to the containing class only. Members marked as private cannot be accessed from outside the class.

Example:

using System;

public class Car
{
    // Private field
    private string _model;

    // Public method to set the model
    public void SetModel(string model)
    {
        _model = model;
    }

    // Public method to display the model
    public void DisplayModel()
    {
        Console.WriteLine("Car model: " + _model);
    }
}

public class Program
{
    public static void Main()
    {
        Car myCar = new Car();
        myCar.SetModel("Corolla"); // Accessing through public method
        myCar.DisplayModel();

        // The following line would produce a compile-time error
        // myCar._model = "Civic"; // Error: _model is private
    }
}

In this example, the _model field is private, so it can only be accessed and modified within the Car class. Outside the class, the field is not accessible directly.

3. protected

The protected access specifier allows access to members from within the containing class and from derived classes. It cannot be accessed from non-derived classes.

Example:

using System;

public class Vehicle
{
    // Protected field
    protected string _engineType;

    // Protected method
    protected void ShowEngineType()
    {
        Console.WriteLine("Engine type: " + _engineType);
    }
}

public class Car : Vehicle
{
    public void SetEngineType(string engineType)
    {
        _engineType = engineType; // Accessing protected field
    }

    public void DisplayEngineType()
    {
        ShowEngineType(); // Calling protected method
    }
}

public class Program
{
    public static void Main()
    {
        Car myCar = new Car();
        myCar.SetEngineType("V8");
        myCar.DisplayEngineType();

        // The following line would produce a compile-time error
        // myCar._engineType = "V6"; // Error: _engineType is protected
    }
}

Here, _engineType and ShowEngineType() are protected, so they can be accessed within the Car class which derives from Vehicle, but not from other classes.

4. internal

The internal access specifier makes members accessible only within the same assembly. It is often used to hide implementation details from other assemblies while still allowing access within the assembly.

Example:

using System;

internal class InternalClass
{
    // Internal field
    internal string Data;

    // Internal method
    internal void DisplayData()
    {
        Console.WriteLine("Data: " + Data);
    }
}

public class Program
{
    public static void Main()
    {
        InternalClass obj = new InternalClass();
        obj.Data = "Some data"; // Accessing internal field
        obj.DisplayData();     // Calling internal method
    }
}

In this example, InternalClass and its members are internal, so they are only accessible within the same assembly.

5. protected internal

The protected internal access specifier is a combination of protected and internal. It allows access from within the same assembly or from derived classes in other assemblies.

Example:

using System;

public class BaseClass
{
    // Protected internal field
    protected internal string Info;

    // Protected internal method
    protected internal void ShowInfo()
    {
        Console.WriteLine("Info: " + Info);
    }
}

public class DerivedClass : BaseClass
{
    public void SetInfo(string info)
    {
        Info = info; // Accessing protected internal field
    }

    public void DisplayInfo()
    {
        ShowInfo(); // Calling protected internal method
    }
}

public class Program
{
    public static void Main()
    {
        DerivedClass obj = new DerivedClass();
        obj.SetInfo("Protected Internal Data");
        obj.DisplayInfo();
    }
}

In this example, Info and ShowInfo() are protected internal, so they can be accessed within the same assembly and by derived classes in other assemblies.

6. private protected (C# 7.2 and later)

The private protected access specifier is more restrictive than protected internal. It allows access only from within the same assembly and derived classes within the same assembly.

Example:

using System;

public class BaseClass
{
    // Private protected field
    private protected string Secret;

    // Private protected method
    private protected void RevealSecret()
    {
        Console.WriteLine("Secret: " + Secret);
    }
}

public class DerivedClass : BaseClass
{
    public void SetSecret(string secret)
    {
        Secret = secret; // Accessing private protected field
    }

    public void ShowSecret()
    {
        RevealSecret(); // Calling private protected method
    }
}

public class Program
{
    public static void Main()
    {
        DerivedClass obj = new DerivedClass();
        obj.SetSecret("Very Private Data");
        obj.ShowSecret();
    }
}

Here, Secret and RevealSecret() are private protected, so they are accessible only within the same assembly and by derived classes within the same assembly.

Summary

  • public: Accessible from anywhere.

  • private: Accessible only within the containing class.

  • protected: Accessible within the containing class and derived classes.

  • internal: Accessible only within the same assembly.

  • protected internal: Accessible within the same assembly and by derived classes.

  • private protected: Accessible within the same assembly and by derived classes within the same assembly.

These access specifiers help in designing classes and their members with proper encapsulation and access control.

0
Subscribe to my newsletter

Read articles from Mahir barot directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Mahir barot
Mahir barot