Language
日本語
English

Caution

JavaScript is disabled in your browser.
This site uses JavaScript for features such as search.
For the best experience, please enable JavaScript before browsing this site.

C# Dictionary

  1. Home
  2. C# Dictionary
  3. throw / Custom Exceptions

throw / Custom Exceptions

Since: C# 1.0(2002)

The throw keyword intentionally raises an exception, and a custom exception class lets you carry application-specific error information.

Syntax

throw new ExceptionType("message");

class ClassName : Exception
{
    public ClassName(string message) : base(message) { }
}

Syntax / Member List

Syntax / MemberDescription
throw new Exception("msg")Throws an exception. Interrupts the current method call and transfers control to the nearest catch block.
throw;Used inside a catch block to rethrow the caught exception while preserving the original stack trace.
ExceptionThe base class for all exceptions.
ArgumentExceptionThrown when an argument is invalid.
ArgumentNullExceptionThrown when an argument is null.
InvalidOperationExceptionThrown when an operation is invalid for the current state of an object.
NotImplementedExceptionUsed to indicate that a method has not been implemented yet.

Sample Code

Program.cs
using System;

// Throw an exception with throw.
static void CheckAge(int age)
{
    if (age < 0 || age > 150)
        throw new ArgumentOutOfRangeException(nameof(age), "Age must be between 0 and 150.");
    Console.WriteLine($"Age: {age}");
}

try
{
    CheckAge(200);
}
catch (ArgumentOutOfRangeException e)
{
    Console.WriteLine(e.Message);
}

class AppException : Exception
{
    public int StatusCode { get; }

    public AppException(string message, int statusCode) : base(message)
    {
        StatusCode = statusCode;
    }
}

// Throw and catch a custom exception.
try
{
    throw new AppException("Authentication failed.", 401);
}
catch (AppException e)
{
    Console.WriteLine($"[{e.StatusCode}] {e.Message}");
}

// Rethrow inside a catch block (preserves the stack trace).
static void Process()
{
    try
    {
        int result = int.Parse("abc");
    }
    catch (FormatException)
    {
        Console.WriteLine("Log: Conversion error detected.");
        throw; // Rethrow the exception (stack trace is preserved).
    }
}

try
{
    Process();
}
catch (FormatException e)
{
    Console.WriteLine("Caught at upper level: " + e.Message);
}

This produces the following output:

dotnet script throw_custom_exception.csx
Age must be between 0 and 150. (Parameter 'age')
[401] Authentication failed.
Log: Conversion error detected.
Caught at upper level: The input string 'abc' was not in a correct format.

Practical Pattern: Custom Exception with Detailed Information

A custom exception class implementation that carries application-specific error codes and additional information as properties.

CustomExceptions.cs
using System;
using System.Collections.Generic;

// A custom exception representing an API error.
class ApiException : Exception
{
    public int     StatusCode { get; }
    public string  ErrorCode { get; }
    public object? Detail { get; }

    public ApiException(string message, int statusCode, string errorCode, object? detail = null)
        : base(message)
    {
        StatusCode = statusCode;
        ErrorCode = errorCode;
        Detail = detail;
    }
}

// A custom exception representing a validation error.
class ValidationException : Exception
{
    public IReadOnlyList<string> Errors { get; }

    public ValidationException(IReadOnlyList<string> errors)
        : base($"Validation error: {errors.Count} issue(s)")
    {
        Errors = errors;
    }
}

// Simulates an API call.
static string FetchUser(int id)
{
    if (id <= 0)
        throw new ApiException("Invalid ID.", 400, "INVALID_ID", new { Id = id });
    if (id > 100)
        throw new ApiException("User not found.", 404, "USER_NOT_FOUND");
    return id switch
    {
        1 => "user_01",
        2 => "user_02",
        _ => $"User-{id}"
    };
}

// Catching ApiException
try { Console.WriteLine(FetchUser(-1)); }
catch (ApiException e)
{
    Console.WriteLine($"API error [{e.StatusCode}] {e.ErrorCode}: {e.Message}");
    if (e.Detail != null) Console.WriteLine($"  Detail: {e.Detail}");
}

// Catching ValidationException
var errors = new List<string> { "Name is required.", "Age must be between 0 and 150." };
try { throw new ValidationException(errors); }
catch (ValidationException e)
{
    Console.WriteLine(e.Message);
    foreach (string err in e.Errors)
        Console.WriteLine($"  - {err}");
}

This produces the following output:

dotnet script custom_exceptions.csx
API error [400] INVALID_ID: Invalid ID.
  Detail: { Id = -1 }
Validation error: 2 issue(s)
  - Name is required.
  - Age must be between 0 and 150.

Practical Pattern: throw Expressions (C# 7 and Later)

From C# 7 onwards, throw can be used as an expression. Combining it with the ternary operator or the null-coalescing operator makes the code more concise.

ThrowExpression.cs
using System;

// throw expression: combined with the ternary operator.
static int Divide(int a, int b)
    => b == 0
        ? throw new DivideByZeroException("Cannot divide by zero.")
        : a / b;

// throw expression: combined with the null-coalescing operator.
static string GetName(string? name)
    => name ?? throw new ArgumentNullException(nameof(name), "Name is required.");

// throw expression: validation in a property setter.
class MissionRecord
{
    private string _title = string.Empty;

    public string Title
    {
        get => _title;
        set => _title = string.IsNullOrWhiteSpace(value)
            ? throw new ArgumentException("Title cannot be empty.", nameof(value))
            : value;
    }
}

// Tests
Console.WriteLine(Divide(10, 2)); // 5
try { Divide(10, 0); } catch (DivideByZeroException e) { Console.WriteLine(e.Message); }

Console.WriteLine(GetName("sample")); // sample
try { GetName(null); } catch (ArgumentNullException e) { Console.WriteLine(e.Message); }

var mission = new MissionRecord();
try
{
    mission.Title = ""; // The setter throws an exception.
}
catch (ArgumentException e)
{
    Console.WriteLine(e.Message);
}

This produces the following output:

dotnet script throw_expression.csx
5
Cannot divide by zero.
sample
Name is required. (Parameter 'name')
Title cannot be empty. (Parameter 'value')

Common Mistakes

Common Mistake 1: throw e Overwrites the Stack Trace

When rethrowing an exception inside a catch block, writing throw e; overwrites the stack trace with the current location. Use the argument-less throw; to preserve the original stack trace.

using System;

static void Inner() => throw new Exception("Inner error");

static void Outer()
{
    try { Inner(); }
    catch (Exception e)
    {
        // NG: throw e; overwrites the stack trace with the Outer location.
        throw e; // Information about Inner is lost.
    }
}

static void OuterSafe()
{
    try { Inner(); }
    catch
    {
        // OK: throw; preserves the original stack trace.
        throw;
    }
}

Common Mistake 2: Throwing Exception Directly

Throwing the generic Exception class directly makes it impossible for callers to identify the type of error. Choose an appropriate exception class or define a custom one.

using System;

// NG: Generic Exception gives callers no information about what went wrong.
static void ProcessNG(string input)
{
    if (input == null)
        throw new Exception("input is null."); // Nothing useful is conveyed by the type.
}

// OK: Use an exception class that matches the situation.
static void ProcessOK(string? input)
{
    if (input == null)
        throw new ArgumentNullException(nameof(input)); // Clearly indicates a null-argument error.
    if (input.Length == 0)
        throw new ArgumentException("input cannot be empty.", nameof(input));
}

try { ProcessOK(null); }
catch (ArgumentNullException e) { Console.WriteLine(e.Message); }

This produces the following output:

dotnet run
Value cannot be null. (Parameter 'input')

Overview

A custom exception class is defined by inheriting from Exception (or another existing exception class). Adding application-specific error codes or extra information as properties enables more detailed error handling in the catch block.

When rethrowing, writing throw e; overwrites the stack trace with the current position. Use the argument-less throw; when you want to preserve the original stack trace.

For the exception-catching syntax, see try / catch / finally.

If you find any errors or copyright issues, please .