C#

Exceptions, and catching the type you actually expect

try/catch/finally, catching specific types first, and why swallowing an exception is worse than not catching it.

After this lesson you can

  • Write a try/catch/finally and say what each part guarantees
  • Catch a specific exception type instead of a bare catch
  • Explain what a custom exception class is for
try
{
    var result = Divide(a, b);
    Console.WriteLine(result);
}
catch (DivideByZeroException e)
{
    Console.WriteLine($"cannot divide: {e.Message}");
}
finally
{
    Console.WriteLine("always runs");
}

finally runs whether try succeeded, threw an exception that was caught, or threw one that was not — even after a return inside try. It is where cleanup belongs when the type itself is not IDisposable and using does not apply.

A catch clause with no type — catch { ... } — catches everything, including exceptions the code has no idea how to handle correctly. An empty catch block is worse than no catch at all: the program continues as if nothing happened, with whatever caused the exception now silently unaddressed, and no trace of it anywhere.

Try it

The specific catch clause wins, in ordercsharp-9
public static class Solution{    public static string Describe()    {        try        {            int[] nums = { 1, 2, 3 };            return "value=" + nums[5];        }        catch (IndexOutOfRangeException e)        {            return "index: " + e.GetType().Name;        }        catch (Exception e)        {            return "generic: " + e.GetType().Name;        }    }}
What to look for

Catch the most specific type first

try { Process(data); }
catch (FileNotFoundException e) { /* a subclass of IOException */ }
catch (IOException e)            { /* the broader type */ }

C# checks catch clauses top to bottom and runs the first one whose type the thrown exception matches. Putting IOException first would catch everything below it too, and the compiler refuses to compile an unreachable catch clause that follows — the ordering is enforced, not just good style.

Custom exceptions

public class InsufficientFundsException : Exception
{
    public decimal Needed { get; }
    public decimal Available { get; }

    public InsufficientFundsException(decimal needed, decimal available)
        : base($"needed {needed}, had {available}")
    {
        Needed = needed;
        Available = available;
    }
}

A custom exception is a real class, usually with almost no logic in it — its job is to give a catch clause something specific to match, and to carry whatever data the caller needs to react correctly, rather than parsing that information back out of a generic message string.

Try it yourself

2 visible tests · 2 hidden tests

Implement ParseAll(List<string> values). For each string, try int.Parse. Return a Dictionary<string, List<string>> with two keys: "parsed", every value that parsed successfully (as strings, in their original form), and "failed", every value that raised a FormatException. Preserve order within each list, and catch that specific exception type — nothing broader.

  • ParseAll(["1","abc","42","3.5"])
  • ParseAll(["1","2","3"])
Loading editor…

Sign up to check the hidden tests and save your progress. Sign up