404 Not Found

404 Not Found


nginx

Exception Handling

try/catch/finally Basic Mechanism

C# uses three keywords — try, catch, and finally — to build its exception handling mechanism. Code that may throw an exception is placed in the try block, the catch block catches and handles the exception, and the finally block always executes regardless of whether an exception occurred, commonly used for resource cleanup.

CSHARP
try
{
    int result = 10 / 0;
}
catch (DivideByZeroException ex)
{
    Console.WriteLine($"Caught exception: {ex.Message}");
}
finally
{
    Console.WriteLine("finally block always executes");
}
TEXT
Caught exception: Attempted to divide by zero.
finally block always executes

Multiple catch Blocks

You can catch different types of exceptions in order. More specific exception types should be placed before more general ones.

CSHARP
try
{
    int[] arr = { 1, 2, 3 };
    Console.WriteLine(arr[10]);
}
catch (IndexOutOfRangeException ex)
{
    Console.WriteLine($"Index out of bounds: {ex.Message}");
}
catch (Exception ex)
{
    Console.WriteLine($"General exception: {ex.Message}");
}
TEXT
Index out of bounds: Index was outside the bounds of the array.

Common Exception Types

Exception Type Trigger Scenario
FormatException Invalid string format, e.g., int.Parse("abc")
NullReferenceException Accessing a member of a null object
IndexOutOfRangeException Array index out of range
DivideByZeroException Integer divided by zero
OverflowException Arithmetic overflow (in checked context)
FileNotFoundException File does not exist
ArgumentException Invalid method argument

Example

CSHARP
try
{
    int num = int.Parse("hello");
}
catch (FormatException)
{
    Console.WriteLine("Format exception: string cannot be converted to integer");
}

string s = null;
try
{
    Console.WriteLine(s.Length);
}
catch (NullReferenceException)
{
    Console.WriteLine("Null reference exception: object is null");
}
▶ Try it Yourself
TEXT
Format exception: string cannot be converted to integer
Null reference exception: object is null

Exception Class Hierarchy

The inheritance relationship of all exception classes is as follows:

TEXT
Object
  └─ Exception
       └─ SystemException
            ├─ FormatException
            ├─ NullReferenceException
            ├─ IndexOutOfRangeException
            ├─ DivideByZeroException
            ├─ OverflowException
            ├─ FileNotFoundException
            └─ ArgumentException
                 └─ ArgumentNullException

Exception Class Properties

The Exception class provides the following commonly used properties for getting detailed exception information:

Property Description
Message Human-readable text describing the exception
StackTrace Call stack information at the time the exception occurred
InnerException The inner exception that caused the current exception
Source Name of the application or object that threw the exception
TargetSite The method that threw the exception

Example

CSHARP
try
{
    int zero = 0;
    int result = 100 / zero;
}
catch (Exception ex)
{
    Console.WriteLine($"Message: {ex.Message}");
    Console.WriteLine($"Source: {ex.Source}");
    Console.WriteLine($"TargetSite: {ex.TargetSite}");
}
▶ Try it Yourself
TEXT
Message: Attempted to divide by zero.
Source: ConsoleApp
TargetSite: Void Main()

Throwing Exceptions with throw

Use the throw keyword to actively throw an exception. When re-throwing, throw; preserves the original stack trace, while throw ex; resets the stack. Avoid using the latter.

Example

CSHARP
void CheckAge(int age)
{
    if (age < 0)
    {
        throw new ArgumentException("Age cannot be negative", nameof(age));
    }
    Console.WriteLine($"Age: {age}");
}

try
{
    CheckAge(-5);
}
catch (ArgumentException ex)
{
    Console.WriteLine(ex.Message);
}
▶ Try it Yourself
TEXT
Age cannot be negative (Parameter 'age')

Difference Between throw and throw ex

CSHARP
void InnerMethod()
{
    throw new InvalidOperationException("Internal error");
}

void OuterMethod()
{
    try
    {
        InnerMethod();
    }
    catch (Exception ex)
    {
        throw;
    }
}

try
{
    OuterMethod();
}
catch (Exception ex)
{
    Console.WriteLine(ex.StackTrace);
}
TEXT
   at InnerMethod() in Program.cs:line 2
   at OuterMethod() in Program.cs:line 10
💡 Tip: Always use throw; to re-throw exceptions. It preserves the complete call stack, making it easier to locate the root cause. throw ex; truncates the stack to the current method, losing the original exception location information.

Exception Filters

C# 6 introduced exception filters with the when clause, allowing conditions to be added to catch. The exception is only caught when the condition evaluates to true.

Example

CSHARP
try
{
    throw new HttpRequestException("Network timeout, please retry later");
}
catch (HttpRequestException ex) when (ex.Message.Contains("timeout"))
{
    Console.WriteLine("Caught timeout exception, preparing to retry");
}
catch (HttpRequestException ex)
{
    Console.WriteLine($"Other network exception: {ex.Message}");
}
▶ Try it Yourself
TEXT
Caught timeout exception, preparing to retry

Custom Exception Classes

Create custom exception classes by inheriting from Exception. Typically, you need to implement three constructors: a parameterless constructor, a constructor with a message parameter, and a constructor with both message and inner exception parameters.

Example

CSHARP
class InsufficientBalanceException : Exception
{
    public decimal Balance { get; }
    public decimal Amount { get; }

    public InsufficientBalanceException()
        : base("Insufficient balance") { }

    public InsufficientBalanceException(string message)
        : base(message) { }

    public InsufficientBalanceException(string message, Exception innerException)
        : base(message, innerException) { }

    public InsufficientBalanceException(decimal balance, decimal amount)
        : base($"Insufficient balance: current balance {balance}, required {amount}")
    {
        Balance = balance;
        Amount = amount;
    }
}

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

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

    public void Withdraw(decimal amount)
    {
        if (amount > Balance)
        {
            throw new InsufficientBalanceException(Balance, amount);
        }
        Balance -= amount;
        Console.WriteLine($"Withdrawal successful, balance: {Balance}");
    }
}

var account = new BankAccount(100);
try
{
    account.Withdraw(200);
}
catch (InsufficientBalanceException ex)
{
    Console.WriteLine(ex.Message);
    Console.WriteLine($"Balance: {ex.Balance}, attempted withdrawal: {ex.Amount}");
}
▶ Try it Yourself
TEXT
Insufficient balance: current balance 100, required 200
Balance: 100, attempted withdrawal: 200

Exception Handling Best Practices

Catch Specific Exceptions, Not Just Exception

CSHARP
try
{
    int value = int.Parse(input);
    Console.WriteLine(100 / value);
}
catch (FormatException)
{
    Console.WriteLine("Input is not a valid integer");
}
catch (DivideByZeroException)
{
    Console.WriteLine("Cannot input zero");
}
⚠️ Note: Do not use empty catch blocks to swallow exceptions, as this makes problems difficult to diagnose.

Don't Swallow Exceptions

CSHARP
try
{
    File.WriteAllText("data.txt", content);
}
catch (Exception)
{
}

The above code hides all errors and is a bad practice. At minimum, you should log the error or re-throw it.

Use finally to Release Resources

CSHARP
FileStream fs = null;
try
{
    fs = new FileStream("data.txt", FileMode.Open);
    int b = fs.ReadByte();
}
finally
{
    fs?.Dispose();
}

Use using Statement Instead of try-finally

Resources that implement the IDisposable interface should prefer the using statement, which automatically calls Dispose at the end of the scope, ensuring resource release even if an exception occurs.

CSHARP
using (var fs = new FileStream("data.txt", FileMode.Open))
{
    int b = fs.ReadByte();
}
📌 Key Point: The using statement is equivalent to try-finally with a Dispose() call and is the preferred approach for resource management.

Wrap Exceptions with InnerException

CSHARP
try
{
    SaveToFile();
}
catch (IOException ioEx)
{
    throw new ApplicationException("Failed to save data", ioEx);
}

By using InnerException, the original exception information is preserved without losing context.

❓ FAQ

Q What is the difference between throw; and throw ex;?
A throw; preserves the original stack trace; throw ex; resets the stack to the current method. Use throw; when debugging.
Q When will the finally block not execute?
A When the program is forcibly terminated (e.g., Environment.FailFast) or a StackOverflowException occurs, finally may not execute.
Q Can you have try-finally without catch?
A Yes. This approach doesn't handle the exception but ensures resource cleanup; the exception continues to propagate upward.
Q Must custom exception classes inherit from Exception?
A Yes, all exception classes must inherit from Exception (directly or indirectly) to be caught by catch.
Q What are the advantages of exception filter when?
A The when clause does not catch the exception when the condition doesn't match, allowing the exception to continue propagating. This is more efficient and semantically clearer than checking conditions inside catch.

📖 Summary

📝 Exercises

  1. Write a method int SafeParse(string s) that uses try-catch to handle FormatException, returning 0 and printing a warning when parsing fails
  2. Write a custom exception InvalidGradeException with a Grade property that is thrown when the grade is not in the 0-100 range
  3. Write code demonstrating the difference between throw; and throw ex;, printing both StackTrace values and observing the difference
  4. Use exception filter when to implement: catch FileNotFoundException only when the file path starts with "config", otherwise let it propagate
  5. Write a file reading method that uses the using statement to ensure StreamReader is properly released, handling possible FileNotFoundException and IOException
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏