PYnative

Python Programming

  • Learn Python ▼
    • Python Tutorials
    • Python Basics
    • Python Interview Q&As
  • Exercises ▼
    • Python Exercises
    • C++ Exercises
    • C Programming Exercises
    • Java Exercises
    • C# Exercises
  • Quizzes
  • Code Editor ▼
    • Online Python Code Editor
    • Online C Compiler
    • Online C++ Compiler
    • Online Java Compiler
    • Online C# Compiler
Home » C# Exercises » C# Exception Handling Exercises: 20 Coding Problems with Solutions

C# Exception Handling Exercises: 20 Coding Problems with Solutions

Updated on: July 15, 2026 | Leave a Comment

Robust C# programs need to handle unexpected situations gracefully, and that means understanding try, catch, finally, and how to design your own custom exception types.

This collection of 20 C# exception handling exercises covers catching specific exception types, cleaning up resources safely, and throwing meaningful custom exceptions instead of letting programs crash.

Each exercise includes a Practice Problem, Purpose, Hint, and a full Solution with Explanation, so you learn to write code that fails safely and predictably.

Also, See: C# Exercises with  22 topic-wise sets and 620+ practice questions.

+ Table Of Contents (20 Exercises)

Table of contents

  • Exercise 1: Handle a Divide by Zero Error
  • Exercise 2: Handle an Out of Range Array Index
  • Exercise 3: Handle an Invalid Number Format
  • Exercise 4: Handle a Null Reference Error
  • Exercise 5: Catch Any Exception with a Generic Handler
  • Exercise 6: Handle Multiple Exception Types
  • Exercise 7: Guarantee Cleanup with Finally
  • Exercise 8: Validate a Property with a Custom Exception
  • Exercise 9: Detect Integer Overflow
  • Exercise 10: Handle a Missing Dictionary Key
  • Exercise 11: Create a Custom Exception Type
  • Exercise 12: Filter Exceptions with a When Clause
  • Exercise 13: Wrap and Preserve an Inner Exception
  • Exercise 14: Refactor Cleanup Code with a Using Statement
  • Exercise 15: Handle an Invalid Cast Safely
  • Exercise 16: Catch Unhandled Exceptions Globally
  • Exercise 17: Compare the Performance of Try-Catch vs TryParse

Exercise 1: Handle a Divide by Zero Error

Practice Problem: Write a program that prompts the user for two integers and divides them. Use a try-catch block to handle DivideByZeroException if the user enters 0 as the divisor.

Purpose: This exercise helps you practice wrapping risky arithmetic in a try-catch block so a predictable error like division by zero does not crash the entire program.

Given Input: numerator = 10, divisor = 0

Expected Output: Error: Cannot divide by zero. Attempted to divide by zero.

▼ Hint
  • Place the division inside a try block, then add a catch (DivideByZeroException ex) block after it.
  • Use ex.Message inside the catch block to include the built-in exception message in your own error output.
▼ Solution & Explanation
using System;

namespace FileHandlingExercises
{
    class Program
    {
        static void Main(string[] args)
        {
            int numerator = 10;
            int divisor = 0;

            try
            {
                int result = numerator / divisor;
                Console.WriteLine($"Result = {result}");
            }
            catch (DivideByZeroException ex)
            {
                Console.WriteLine($"Error: Cannot divide by zero. {ex.Message}");
            }
        }
    }
}Code language: C# (cs)

Explanation:

  • numerator / divisor: Integer division by zero throws a DivideByZeroException at runtime instead of returning a value like floating point division would.
  • catch (DivideByZeroException ex): Catches specifically this exception type, letting the program respond with a clear message instead of terminating.
  • ex.Message: Exposes the built-in description of what went wrong, which can be combined with your own custom text.

Exercise 2: Handle an Out of Range Array Index

Practice Problem: Create an array of 5 colors. Ask the user to input an index to print a color. Handle IndexOutOfRangeException gracefully if they pick an index like 10.

Purpose: This exercise helps you practice catching IndexOutOfRangeException, which is thrown whenever code accesses an array position that does not exist.

Given Input: colors = { "Red", "Green", "Blue", "Yellow", "Purple" }, index = 10

Expected Output: Error: Index 10 is out of range. Index was outside the bounds of the array.

▼ Hint
  • Wrap the array access, colors[index], inside a try block.
  • Add a catch (IndexOutOfRangeException ex) block to print a friendly message instead of letting the program crash.
▼ Solution & Explanation
using System;

namespace FileHandlingExercises
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] colors = { "Red", "Green", "Blue", "Yellow", "Purple" };
            int index = 10;

            try
            {
                Console.WriteLine($"Color = {colors[index]}");
            }
            catch (IndexOutOfRangeException ex)
            {
                Console.WriteLine($"Error: Index {index} is out of range. {ex.Message}");
            }
        }
    }
}Code language: C# (cs)

Explanation:

  • colors[index]: Accessing index 10 on a 5 element array is invalid, since valid indexes only range from 0 to 4.
  • catch (IndexOutOfRangeException ex): Intercepts the runtime error and lets the program report the invalid index instead of terminating unexpectedly.

Exercise 3: Handle an Invalid Number Format

Practice Problem: Write a program that asks the user for their age. Use int.Parse(). Catch FormatException if they type “twenty” instead of a number.

Purpose: This exercise helps you practice catching FormatException, which is thrown whenever int.Parse() receives text that cannot be converted into a number.

Given Input: ageInput = "twenty"

Expected Output: Error: "twenty" is not a valid number. The input string 'twenty' was not in a correct format.

▼ Hint
  • Call int.Parse(ageInput) inside a try block rather than int.TryParse(), since Parse() is what actually throws the exception.
  • Catch FormatException specifically to handle the case where the text does not represent a valid integer.
▼ Solution & Explanation
using System;

namespace FileHandlingExercises
{
    class Program
    {
        static void Main(string[] args)
        {
            string ageInput = "twenty";

            try
            {
                int age = int.Parse(ageInput);
                Console.WriteLine($"Age = {age}");
            }
            catch (FormatException ex)
            {
                Console.WriteLine($"Error: \"{ageInput}\" is not a valid number. {ex.Message}");
            }
        }
    }
}Code language: C# (cs)

Explanation:

  • int.Parse(ageInput): Attempts to convert the text into an integer, throwing a FormatException immediately when the text is not a valid number.
  • catch (FormatException ex): Catches the parsing failure so the program can explain the problem instead of crashing.

Exercise 4: Handle a Null Reference Error

Practice Problem: Create a string variable initialized to null. Try to invoke .ToUpper() on it. Wrap it in a try-catch to handle NullReferenceException.

Purpose: This exercise helps you practice recognizing and catching NullReferenceException, one of the most common runtime errors, caused by calling a method on a variable that holds no object.

Given Input: name = null

Expected Output: Error: Cannot call a method on a null reference. Object reference not set to an instance of an object.

▼ Hint
  • Call name.ToUpper() inside a try block, where name is assigned null instead of an actual string.
  • Catch NullReferenceException to intercept the failure that occurs from calling a method on a null value.
▼ Solution & Explanation
using System;

namespace FileHandlingExercises
{
    class Program
    {
        static void Main(string[] args)
        {
            string name = null;

            try
            {
                string upperName = name.ToUpper();
                Console.WriteLine($"Upper Name = {upperName}");
            }
            catch (NullReferenceException ex)
            {
                Console.WriteLine($"Error: Cannot call a method on a null reference. {ex.Message}");
            }
        }
    }
}Code language: C# (cs)

Explanation:

  • name.ToUpper(): Calling any method on a variable holding null throws a NullReferenceException, since there is no actual object to call the method on.
  • catch (NullReferenceException ex): Catches the failure so the program can report it clearly instead of terminating with an unhandled error.

Exercise 5: Catch Any Exception with a Generic Handler

Practice Problem: Write a program that simulates a random error generator (throwing different exceptions). Use a generic catch (Exception ex) block to log the error message and the exception type to the console.

Purpose: This exercise helps you practice writing a single, general purpose catch block that can handle any exception type, along with inspecting the exception’s runtime type using GetType().

Given Input: errorChoice = 2, which triggers an ArgumentException

Expected Output:

Caught Exception Type = ArgumentException
Message = Invalid argument provided.
▼ Hint
  • Write a helper method that throws a different exception type depending on an input value, using a switch statement.
  • Catch everything with a single catch (Exception ex) block, since it is the base type that every exception inherits from.
  • Use ex.GetType().Name to print the specific exception class that was actually thrown and caught.
▼ Solution & Explanation
using System;

namespace FileHandlingExercises
{
    class Program
    {
        static void ThrowRandomError(int choice)
        {
            switch (choice)
            {
                case 1:
                    throw new InvalidOperationException("Invalid operation occurred.");
                case 2:
                    throw new ArgumentException("Invalid argument provided.");
                default:
                    throw new Exception("An unexpected error occurred.");
            }
        }

        static void Main(string[] args)
        {
            int errorChoice = 2;

            try
            {
                ThrowRandomError(errorChoice);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Caught Exception Type = {ex.GetType().Name}");
                Console.WriteLine($"Message = {ex.Message}");
            }
        }
    }
}Code language: C# (cs)

Explanation:

  • catch (Exception ex): Since every exception type ultimately inherits from Exception, this single block can catch any of them.
  • ex.GetType().Name: Returns the actual runtime class name of the caught exception, even though the variable is declared as the general Exception type.
  • throw new ArgumentException("Invalid argument provided."): Creates and throws a specific exception type with a custom message describing what went wrong.

Exercise 6: Handle Multiple Exception Types

Practice Problem: Write a method that reads a file path from the user, opens the file, and parses the first line into an integer. Implement multiple catch blocks to handle FileNotFoundException, FormatException, and a generic Exception.

Purpose: This exercise helps you practice stacking multiple catch blocks in order from most specific to least specific, so each type of failure gets its own tailored message.

Given Input: filePath = "data.txt" (a file that does not exist at that location)

Expected Output: Error: File not found. Could not find file 'data.txt'.

Note: the exact output depends on whether data.txt exists when the program runs. If the file exists but its first line is not a number, the FormatException block runs instead.

▼ Hint
  • Order the catch blocks from most specific to least specific: FileNotFoundException, then FormatException, then Exception.
  • Use a using statement around the StreamReader so the file is disposed of automatically once reading finishes or an exception occurs.
▼ Solution & Explanation
using System;
using System.IO;

namespace FileHandlingExercises
{
    class Program
    {
        static void ReadFirstLineAsInt(string filePath)
        {
            try
            {
                using (StreamReader reader = new StreamReader(filePath))
                {
                    string firstLine = reader.ReadLine();
                    int value = int.Parse(firstLine);
                    Console.WriteLine($"Parsed Value = {value}");
                }
            }
            catch (FileNotFoundException ex)
            {
                Console.WriteLine($"Error: File not found. {ex.Message}");
            }
            catch (FormatException ex)
            {
                Console.WriteLine($"Error: File content is not a valid number. {ex.Message}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Unexpected Error: {ex.Message}");
            }
        }

        static void Main(string[] args)
        {
            string filePath = "data.txt";
            ReadFirstLineAsInt(filePath);
        }
    }
}Code language: C# (cs)

Explanation:

  • using (StreamReader reader = new StreamReader(filePath)): Opens the file for reading and guarantees the reader is closed automatically once the block finishes, whether or not an exception occurs.
  • Catch order: The catch blocks are listed from most specific (FileNotFoundException) to least specific (Exception), which is required since C# checks them top to bottom.
  • catch (Exception ex): Acts as a final safety net for any error that does not match the two more specific catch blocks above it.

Exercise 7: Guarantee Cleanup with Finally

Practice Problem: Write a program that opens a StreamWriter to write data to a file. Use a try-finally (or try-catch-finally) block to ensure the writer is explicitly closed and disposed of, even if an error occurs during writing.

Purpose: This exercise helps you practice using a finally block to guarantee cleanup code always runs, whether the try block succeeds or throws an exception.

Given Input: filePath = "output.txt"

Expected Output:

Data written successfully.
Writer closed.
▼ Hint
  • Declare the StreamWriter variable above the try block so it is still in scope inside the finally block.
  • In the finally block, check whether the writer is not null before calling .Close() on it.
▼ Solution & Explanation
using System;
using System.IO;

namespace FileHandlingExercises
{
    class Program
    {
        static void Main(string[] args)
        {
            StreamWriter writer = null;
            string filePath = "output.txt";

            try
            {
                writer = new StreamWriter(filePath);
                writer.WriteLine("Writing data to the file.");
                Console.WriteLine("Data written successfully.");
            }
            catch (IOException ex)
            {
                Console.WriteLine($"Error: Could not write to file. {ex.Message}");
            }
            finally
            {
                if (writer != null)
                {
                    writer.Close();
                    Console.WriteLine("Writer closed.");
                }
            }
        }
    }
}Code language: C# (cs)

Explanation:

  • finally: The code inside this block always runs after the try block finishes, regardless of whether an exception was thrown or caught.
  • if (writer != null): Guards against calling .Close() on a writer that was never successfully created, for example if the file path itself was invalid.
  • writer.Close(): Flushes any buffered data to disk and releases the file handle, ensuring the file is not left open.

Exercise 8: Validate a Property with a Custom Exception

Practice Problem: Create a Person class with an Age property. In the setter, if the age is less than 0 or greater than 120, throw an ArgumentOutOfRangeException with a custom message.

Purpose: This exercise helps you practice throwing your own exception from inside a property setter, so invalid state can never be assigned to an object in the first place.

Given Input: person.Age = 150

Expected Output: Error: Age must be between 0 and 120. (Parameter 'value')

▼ Hint
  • Inside the set accessor, check value < 0 || value > 120 before assigning it to the backing field.
  • Use throw new ArgumentOutOfRangeException(nameof(value), "your message") to include both the parameter name and a custom explanation.
▼ Solution & Explanation
using System;

namespace FileHandlingExercises
{
    class Person
    {
        private int age;

        public int Age
        {
            get { return age; }
            set
            {
                if (value < 0 || value > 120)
                {
                    throw new ArgumentOutOfRangeException(nameof(value), "Age must be between 0 and 120.");
                }

                age = value;
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Person person = new Person();

            try
            {
                person.Age = 150;
            }
            catch (ArgumentOutOfRangeException ex)
            {
                Console.WriteLine($"Error: {ex.Message}");
            }
        }
    }
}Code language: C# (cs)

Explanation:

  • if (value < 0 || value > 120): Validates the incoming value inside the setter before it is ever stored in the backing field.
  • throw new ArgumentOutOfRangeException(nameof(value), "..."): Throws a built-in exception type intended specifically for values that fall outside an acceptable range, paired with a custom explanation.
  • nameof(value): Refers to the setter’s implicit value parameter by name, so the exception clearly identifies which parameter caused the failure.

Exercise 9: Detect Integer Overflow

Practice Problem: Write a program that multiplies two large integers (e.g., int.MaxValue * 2) inside a checked block. Catch the resulting OverflowException.

Purpose: This exercise helps you practice using the checked keyword to force arithmetic overflow to throw an exception instead of silently wrapping around to an incorrect value.

Given Input: int.MaxValue * 2

Expected Output: Error: Arithmetic operation overflowed. Arithmetic operation resulted in an overflow.

▼ Hint
  • Wrap the multiplication in a checked { ... } block, since normal unchecked arithmetic silently overflows without throwing.
  • Catch OverflowException around the checked block to handle the case where the result is too large to fit in an int.
▼ Solution & Explanation
using System;

namespace FileHandlingExercises
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                checked
                {
                    int result = int.MaxValue * 2;
                    Console.WriteLine($"Result = {result}");
                }
            }
            catch (OverflowException ex)
            {
                Console.WriteLine($"Error: Arithmetic operation overflowed. {ex.Message}");
            }
        }
    }
}Code language: C# (cs)

Explanation:

  • checked { ... }: Forces the enclosed arithmetic to be checked for overflow at runtime, throwing an exception instead of wrapping the value around silently.
  • int.MaxValue * 2: Multiplying the largest possible int value by 2 produces a result that cannot fit within the 32-bit range of int.
  • catch (OverflowException ex): Catches the overflow specifically, allowing the program to report it clearly instead of continuing with a corrupted value.

Exercise 10: Handle a Missing Dictionary Key

Practice Problem: Create a lookup dictionary. Try to access a key that doesn’t exist. Catch the KeyNotFoundException and display a friendly message showing available keys.

Purpose: This exercise helps you practice catching KeyNotFoundException, thrown when indexing directly into a dictionary with a key that is not present, and using the caught error to guide the user toward valid options.

Given Input: A dictionary with keys Keyboard, Mouse, and Monitor. requestedKey = "Webcam"

Expected Output:

Error: Key "Webcam" was not found. The given key 'Webcam' was not present in the dictionary.
Available Keys = Keyboard, Mouse, Monitor
▼ Hint
  • Accessing a dictionary with square bracket syntax, stock[requestedKey], throws KeyNotFoundException when the key is missing, unlike TryGetValue() which does not throw.
  • Inside the catch block, use string.Join(", ", stock.Keys) to list all the keys that are actually available.
▼ Solution & Explanation
using System;
using System.Collections.Generic;
using System.Linq;

namespace FileHandlingExercises
{
    class Program
    {
        static void Main(string[] args)
        {
            Dictionary<string, int> stock = new Dictionary<string, int>
            {
                { "Keyboard", 12 },
                { "Mouse", 30 },
                { "Monitor", 5 }
            };

            string requestedKey = "Webcam";

            try
            {
                int quantity = stock[requestedKey];
                Console.WriteLine($"Quantity = {quantity}");
            }
            catch (KeyNotFoundException ex)
            {
                Console.WriteLine($"Error: Key \"{requestedKey}\" was not found. {ex.Message}");
                Console.WriteLine($"Available Keys = {string.Join(", ", stock.Keys)}");
            }
        }
    }
}Code language: C# (cs)

Explanation:

  • stock[requestedKey]: Indexing directly into the dictionary throws KeyNotFoundException when the requested key is not present.
  • string.Join(", ", stock.Keys): Builds a readable, comma separated list of every key currently in the dictionary to help guide the user.
  • Alternative: Using stock.TryGetValue(requestedKey, out int quantity) instead would avoid the exception entirely, but this exercise focuses specifically on catching KeyNotFoundException.

Exercise 11: Create a Custom Exception Type

Practice Problem: Create a banking application simulation. Write a custom exception named InsufficientFundsException. Throw it when a user tries to withdraw more money than their current balance.

Purpose: This exercise helps you practice defining your own exception class by inheriting from Exception, giving you a domain specific error type instead of relying only on built-in exceptions.

Given Input: Balance = 100, withdrawal amount = 250

Expected Output: Error: Cannot withdraw $250. Current balance is $100.

▼ Hint
  • Create a class named InsufficientFundsException that inherits from Exception, with a constructor that passes its message to the base class.
  • In the Withdraw() method, compare the requested amount to the current balance before subtracting it, and throw the custom exception if the amount is too high.
▼ Solution & Explanation
using System;

namespace FileHandlingExercises
{
    class InsufficientFundsException : Exception
    {
        public InsufficientFundsException(string message) : base(message)
        {
        }
    }

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

        public BankAccount(decimal initialBalance)
        {
            Balance = initialBalance;
        }

        public void Withdraw(decimal amount)
        {
            if (amount > Balance)
            {
                throw new InsufficientFundsException($"Cannot withdraw ${amount}. Current balance is ${Balance}.");
            }

            Balance -= amount;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            BankAccount account = new BankAccount(100);

            try
            {
                account.Withdraw(250);
            }
            catch (InsufficientFundsException ex)
            {
                Console.WriteLine($"Error: {ex.Message}");
            }
        }
    }
}Code language: C# (cs)

Explanation:

  • class InsufficientFundsException : Exception: Defines a new exception type by inheriting from the base Exception class, gaining all of its standard behavior.
  • : base(message): Forwards the custom message up to the base Exception constructor, so it becomes available through the familiar ex.Message property.
  • catch (InsufficientFundsException ex): Catches the custom exception type specifically, the same way you would catch any built-in exception.

Exercise 12: Filter Exceptions with a When Clause

Practice Problem: Write a program that simulates an HTTP web request failure. Throw an exception with an error code, and use the catch (Exception ex) when (...) syntax to only catch the exception if the error code is 404.

Purpose: This exercise helps you practice exception filters, which let a catch block run only when an extra condition is true, instead of catching every exception of that type.

Given Input: errorCode = 404

Expected Output: Error: Resource not found (404). The HTTP request failed.

▼ Hint
  • Give your custom exception an ErrorCode property so the filter has something to check against.
  • Write the filter as catch (HttpRequestFailedException ex) when (ex.ErrorCode == 404), placed before a broader catch block for the same exception type.
▼ Solution & Explanation
using System;

namespace FileHandlingExercises
{
    class HttpRequestFailedException : Exception
    {
        public int ErrorCode { get; }

        public HttpRequestFailedException(string message, int errorCode) : base(message)
        {
            ErrorCode = errorCode;
        }
    }

    class Program
    {
        static void SimulateRequest(int errorCode)
        {
            throw new HttpRequestFailedException("The HTTP request failed.", errorCode);
        }

        static void Main(string[] args)
        {
            try
            {
                SimulateRequest(404);
            }
            catch (HttpRequestFailedException ex) when (ex.ErrorCode == 404)
            {
                Console.WriteLine($"Error: Resource not found (404). {ex.Message}");
            }
            catch (HttpRequestFailedException ex)
            {
                Console.WriteLine($"Error: Request failed with code {ex.ErrorCode}. {ex.Message}");
            }
        }
    }
}Code language: C# (cs)

Explanation:

  • when (ex.ErrorCode == 404): Adds an extra condition to the catch block, so it only activates when the exception’s ErrorCode is specifically 404.
  • Catch order: Since both catch blocks handle the same exception type, the filtered one must appear first, otherwise it would never be reached.
  • Unmatched filter: If the filter condition evaluates to false, execution falls through to the next matching catch block instead of the filtered one.

Exercise 13: Wrap and Preserve an Inner Exception

Practice Problem: Write a method DataLayer() that throws a SqlException (or a simulated database error). Call it from a BusinessLayer() method, catch it, wrap it inside a new ApplicationException, and throw the new one while preserving the original as the InnerException.

Purpose: This exercise helps you practice wrapping a low level exception in a higher level, more meaningful exception without losing the original error details needed for debugging.

Given Input: DataLayer() simulates a database connection failure instead of using an actual SqlException, since that type requires a separate database library reference.

Expected Output:

Outer Message = A business layer error occurred while accessing data.
Inner Message = Simulated database connection failure.
▼ Hint
  • Catch the low level exception inside BusinessLayer(), then construct a new ApplicationException passing the original exception as its second constructor argument.
  • The ApplicationException(string message, Exception innerException) constructor automatically stores the original exception in the InnerException property.
▼ Solution & Explanation
using System;

namespace FileHandlingExercises
{
    class Program
    {
        static void DataLayer()
        {
            throw new InvalidOperationException("Simulated database connection failure.");
        }

        static void BusinessLayer()
        {
            try
            {
                DataLayer();
            }
            catch (InvalidOperationException ex)
            {
                throw new ApplicationException("A business layer error occurred while accessing data.", ex);
            }
        }

        static void Main(string[] args)
        {
            try
            {
                BusinessLayer();
            }
            catch (ApplicationException ex)
            {
                Console.WriteLine($"Outer Message = {ex.Message}");
                Console.WriteLine($"Inner Message = {ex.InnerException.Message}");
            }
        }
    }
}Code language: C# (cs)

Explanation:

  • throw new ApplicationException("...", ex): Creates a new, higher level exception while attaching the original exception ex as its InnerException.
  • ex.InnerException.Message: Reaches back through the wrapper to read the message from the original low level exception.
  • Preserved context: Wrapping the exception this way means callers of BusinessLayer() see a meaningful, high level error while the original cause is never lost.

Exercise 14: Refactor Cleanup Code with a Using Statement

Practice Problem: Take a standard try-catch-finally block used for database connections or file I/O, and refactor it using the modern using statement/declaration to demonstrate implicit exception-safe resource disposal.

Purpose: This exercise helps you practice replacing a manual try-finally cleanup pattern with a using block, which disposes of the resource automatically even if an exception is thrown.

Given Input: filePath = "log.txt"

Expected Output:

Log entry written successfully.
Writer disposed automatically.
▼ Hint
  • The manual version would declare the writer above a try block, use it inside, and call .Dispose() or .Close() inside a finally block.
  • Replace all of that with using (StreamWriter writer = new StreamWriter(filePath)) { ... }, since StreamWriter implements IDisposable and gets disposed automatically once the block ends.
▼ Solution & Explanation
using System;
using System.IO;

namespace FileHandlingExercises
{
    class Program
    {
        static void Main(string[] args)
        {
            string filePath = "log.txt";

            using (StreamWriter writer = new StreamWriter(filePath))
            {
                writer.WriteLine("Application started.");
                Console.WriteLine("Log entry written successfully.");
            }

            Console.WriteLine("Writer disposed automatically.");
        }
    }
}Code language: C# (cs)

Explanation:

  • using (StreamWriter writer = ...): Replaces the manual try-finally pattern entirely, since the compiler generates the equivalent cleanup logic automatically.
  • Automatic disposal: Once execution leaves the using block, whether normally or due to an exception, writer.Dispose() is called behind the scenes.
  • Less boilerplate: The refactored version removes the need for a nullable variable declared outside the block and a manual null check inside finally.

Exercise 15: Handle an Invalid Cast Safely

Practice Problem: Create a list of objects containing a mix of strings, integers, and custom classes. Loop through the list and try to explicitly cast every object to a string. Handle InvalidCastException for the non-string items so the loop continues.

Purpose: This exercise helps you practice catching InvalidCastException inside a loop, so a single bad cast does not stop the entire iteration from completing.

Given Input: items = { "Apple", 42, new Product { Name = "Chair" }, "Banana" }

Expected Output:

Valid String = Apple
Skipped Item: Cannot cast Int32 to string. Unable to cast object of type 'System.Int32' to type 'System.String'.
Skipped Item: Cannot cast Product to string. Unable to cast object of type 'FileHandlingExercises.Product' to type 'System.String'.
Valid String = Banana
▼ Hint
  • Store the mixed values in a List<object>, since it can hold any type.
  • Inside the foreach loop, wrap the explicit cast (string)item in its own try-catch so one failed cast does not exit the loop early.
▼ Solution & Explanation
using System;
using System.Collections.Generic;

namespace FileHandlingExercises
{
    class Product
    {
        public string Name { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            List<object> items = new List<object> { "Apple", 42, new Product { Name = "Chair" }, "Banana" };

            foreach (object item in items)
            {
                try
                {
                    string text = (string)item;
                    Console.WriteLine($"Valid String = {text}");
                }
                catch (InvalidCastException ex)
                {
                    Console.WriteLine($"Skipped Item: Cannot cast {item.GetType().Name} to string. {ex.Message}");
                }
            }
        }
    }
}Code language: C# (cs)

Explanation:

  • (string)item: An explicit cast that throws InvalidCastException at runtime whenever the underlying object is not actually a string.
  • try-catch inside the loop: Placing the try-catch inside the foreach body, rather than around the whole loop, allows every remaining item to still be processed after a failed cast.
  • item.GetType().Name: Reports the actual runtime type of the skipped item, making the error message more informative than a generic failure notice.

Exercise 16: Catch Unhandled Exceptions Globally

Practice Problem: Create a console application or a small Web API project and implement a global unhandled exception handler (e.g., using AppDomain.CurrentDomain.UnhandledException or an ASP.NET Core Middleware) to catch errors that escape local try-catch blocks.

Purpose: This exercise helps you practice subscribing to AppDomain.CurrentDomain.UnhandledException, a last resort notification point for exceptions that were never caught anywhere else in the application.

Given Input: ThrowUnhandledError() throws an exception with no surrounding try-catch.

Expected Output:

Application starting.
Global Handler Caught: Something went wrong outside any try-catch block.

Note: AppDomain.CurrentDomain.UnhandledException only notifies you that an unhandled exception occurred, it does not stop the process from terminating afterward. It is meant for logging and diagnostics, not for recovering execution.

▼ Hint
  • Subscribe to AppDomain.CurrentDomain.UnhandledException with a lambda near the very start of Main(), before anything risky runs.
  • The event handler receives an UnhandledExceptionEventArgs, whose ExceptionObject property must be cast to Exception to read its message.
▼ Solution & Explanation
using System;

namespace FileHandlingExercises
{
    class Program
    {
        static void Main(string[] args)
        {
            AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
            {
                Exception ex = (Exception)e.ExceptionObject;
                Console.WriteLine($"Global Handler Caught: {ex.Message}");
            };

            Console.WriteLine("Application starting.");
            ThrowUnhandledError();
        }

        static void ThrowUnhandledError()
        {
            throw new Exception("Something went wrong outside any try-catch block.");
        }
    }
}Code language: C# (cs)

Explanation:

  • AppDomain.CurrentDomain.UnhandledException += ...: Registers a handler that runs whenever an exception propagates all the way up without being caught anywhere in the application.
  • (Exception)e.ExceptionObject: The event exposes the exception as a plain object, so it needs to be cast back to Exception before reading its Message.
  • Not a substitute for try-catch: This handler is only useful for logging or last-chance diagnostics, since the process still terminates once it finishes running.

Exercise 17: Compare the Performance of Try-Catch vs TryParse

Practice Problem: Write two methods that process 10,000 invalid string inputs into integers. Method A uses try-catch with int.Parse(). Method B uses int.TryParse(). Benchmark the execution time of both to observe the performance cost of throwing thousands of exceptions.

Purpose: This exercise helps you practice measuring the real performance cost of exceptions, since throwing and catching an exception is far more expensive than a simple conditional check.

Given Input: An array of 10,000 copies of the invalid string "not-a-number".

Expected Output:

Method A (try-catch with Parse) = 850 ms
Method B (TryParse) = 4 ms

Note: the exact millisecond values will vary significantly depending on your machine and the .NET runtime version, but Method A should consistently be dramatically slower than Method B.

▼ Hint
  • Use the Stopwatch class from System.Diagnostics to measure elapsed time around each method call.
  • Method A should call int.Parse() inside a try-catch that silently ignores the FormatException, while Method B should call int.TryParse() and ignore its boolean result.
▼ Solution & Explanation
using System;
using System.Diagnostics;

namespace FileHandlingExercises
{
    class Program
    {
        static void ParseWithTryCatch(string[] inputs)
        {
            foreach (string input in inputs)
            {
                try
                {
                    int value = int.Parse(input);
                }
                catch (FormatException)
                {
                    // Ignored for benchmarking purposes.
                }
            }
        }

        static void ParseWithTryParse(string[] inputs)
        {
            foreach (string input in inputs)
            {
                int.TryParse(input, out int value);
            }
        }

        static void Main(string[] args)
        {
            string[] invalidInputs = new string[10000];
            for (int i = 0; i < invalidInputs.Length; i++)
            {
                invalidInputs[i] = "not-a-number";
            }

            Stopwatch tryCatchStopwatch = Stopwatch.StartNew();
            ParseWithTryCatch(invalidInputs);
            tryCatchStopwatch.Stop();

            Stopwatch tryParseStopwatch = Stopwatch.StartNew();
            ParseWithTryParse(invalidInputs);
            tryParseStopwatch.Stop();

            Console.WriteLine($"Method A (try-catch with Parse) = {tryCatchStopwatch.ElapsedMilliseconds} ms");
            Console.WriteLine($"Method B (TryParse) = {tryParseStopwatch.ElapsedMilliseconds} ms");
        }
    }
}Code language: C# (cs)

Explanation:

  • Stopwatch.StartNew(): Creates and immediately starts a timer, providing a simple way to measure how long each method takes to run.
  • int.Parse() inside try-catch: Every single failed parse throws and catches a real exception, and exception handling carries significant overhead compared to normal control flow.
  • int.TryParse(): Reports success or failure through a boolean return value instead of throwing, avoiding exception overhead entirely for expected, everyday invalid input.

Filed Under: C# Exercises

Did you find this page helpful? Let others know about it. Sharing helps me continue to create free Python resources.

TweetF  sharein  shareP  Pin

About Vishal

I’m Vishal Hule, the Founder of PYnative.com. As a Python developer, I enjoy assisting students, developers, and learners. Follow me on Twitter.

Related Tutorial Topics:

C# Exercises

All Coding Exercises:

C Exercises
C++ Exercises
Python Exercises
Java Exercises
C# Exercises

Python Exercises and Quizzes

Free coding exercises and quizzes cover Python basics, data structure, data analytics, and more.

  • 15+ Topic-specific Exercises and Quizzes
  • Each Exercise contains 25+ questions
  • Each Quiz contains 25 MCQ
Exercises
Quizzes

Leave a Reply Cancel reply

your email address will NOT be published. all comments are moderated according to our comment policy.

Use <pre> tag for posting code. E.g. <pre> Your entire code </pre>

In: C# Exercises
TweetF  sharein  shareP  Pin

  C# Exercises

  • C# Exercise for Beginners
  • C# Loops Exercise
  • C# Array Exercise
  • C# String Exercise
  • C# OOP Exercise
  • C# Structs, Records and Enums Exercise
  • C# Collections Exercise
  • C# List Exercise
  • C# Dictionary Exercise
  • C# LINQ Exercise
  • C# Exception Handling Exercise
  • C# File Handling Exercise
  • C# Date and Time Exercise
  • C# Generics Exercise
  • C# Lambda Expressions Exercise
  • C# Delegates and Events Exercise
  • C# Extension Methods Exercise
  • C# Regex Exercise
  • C# Pattern Matching Exercise
  • C# Iterators and Yield Exercise
  • C# Indexers and Operator Overloading Exercise
  • C# Random Data Generation Exercise

All Coding Exercises

Python Exercises C Exercises C++ Exercises Java Exercises C# Exercises

About PYnative

PYnative.com is for Python lovers. Here, You can get Tutorials, Exercises, and Quizzes to practice and improve your Python skills.

Follow Us

To get New Python Tutorials, Exercises, and Quizzes

  • Twitter
  • Facebook
  • Sitemap

Explore Python

  • Learn Python
  • Python Basics
  • Python Databases
  • Python Exercises
  • Python Quizzes
  • Online Python Code Editor
  • Python Tricks

Coding Exercises

  • C Exercises
  • C++ Exercises
  • Python Exercises
  • Java Exercises
  • C# Exercises

Legal Stuff

  • About Us
  • Contact Us

We use cookies to improve your experience. While using PYnative, you agree to have read and accepted our:

  • Terms Of Use
  • Privacy Policy
  • Cookie Policy

Copyright © 2018–2026 pynative.com