Exception Handling
java1-7 positioned Java's checked exceptions as a real middle ground between C++'s unenforced exceptions and Rust's Result<T, E>. C#'s designers looked at that same middle ground and deliberately rejected it — this chapter explains why, with their own stated reasoning.
try/catch/finally — Same Shape
Structurally identical to java1-7 — try, a typed catch, an unconditional finally. The real divergence isn't the syntax.
Every C# Exception Is Unchecked
C# has no checked-exception concept at all. Every exception, without exception, behaves the way java1-7 described for RuntimeException — a method can throw anything, with zero compiler-enforced acknowledgment, ever. No throws clause exists in C#'s syntax to require in the first place.
Why C# Rejected Checked Exceptions
This was a deliberate design decision, not an oversight — Anders Hejlsberg (csharp1-1's own origin-story architect) has spoken publicly about the specific reasoning. Two real, documented critiques of java1-7's own model: checked exceptions don't version well — adding one new checked exception to a widely-used method's signature breaks every existing caller that doesn't already handle it, a real practical cost in library evolution. And in practice, checked exceptions get "handled" via empty catch blocks or wrapped indiscriminately in a generic RuntimeException just to satisfy the compiler — arguably making real code worse, not safer, than leaving the choice to the programmer's judgment.
XML Doc Comments as the Real (Weaker) Substitute
C# lets an API author document what a method might throw via an XML doc comment's <exception> tag, surfaced in IntelliSense — but nothing here is compiler-enforced at all. It's purely informational, a real, direct contrast against java1-7's own compiler-enforced throws clause.
Exception Filters — A Genuinely Unique Addition
catch (...) when (condition) is a real feature with no direct Java equivalent — a catch block only fires when its exception type and a runtime condition both hold, avoiding a nested if statement (and a re-throw of everything that doesn't match) inside a broader catch.
using — C#'s Answer to try-with-resources
Any type implementing IDisposable works with using, calling Dispose() automatically — genuinely comparable to java1-7's try-with-resources/AutoCloseable, and to cpp1-5's RAII. C# 8's using declaration form (shown above, no extra braces) is even more concise syntactically than Java's own block-scoped try(...) syntax.
| Aspect | Java (java1-7) | C# |
|---|---|---|
| Checked exceptions | yes — compiler-enforced catch or throws | no such concept exists |
| Documenting what a method might throw | throws clause — compiler-enforced | XML doc <exception> tag — informational only |
| Conditional catch | not built in — needs a nested if | catch (...) when (condition) — built in |
| Automatic resource cleanup | try-with-resources | using — including a more concise declaration form |
catch (...) when (condition) keeps a narrow, conditional handling case cleanly separate from the general case, without needing an inner if and a manual re-throw to fall through to a second catch block.
java1-7's own compiler-enforced acknowledgment simply has no C# counterpart. Knowing what a method can throw relies entirely on documentation, testing, and discipline, not language enforcement.
Coding Challenges
Write a method that divides by zero inside a try block, catch DivideByZeroException specifically, and use a finally block that always runs. Then write a second method that throws a custom exception with no throws-style declaration anywhere, showing it compiles without any compiler warning at all.
📄 View solutionWrite a method that throws a custom exception with an int ErrorCode property, then use two catch blocks: one with a when filter that only fires for a specific error code, and a second, unfiltered catch for every other case.
📄 View solutionWrite a class implementing IDisposable with a Dispose() method that prints a message, then use it with a using declaration (not a using block) alongside code that throws an exception afterward in the same scope, showing Dispose() still runs.
📄 View solutionChapter 7 Quick Reference
- C# has no checked-exception concept — every exception behaves like java1-7's own RuntimeException, with zero compiler-enforced acknowledgment
- This was a deliberate, documented design choice (Anders Hejlsberg) — checked exceptions version poorly and get swallowed via empty catches/blanket rethrows in practice
- XML doc <exception> tags document possible throws for IntelliSense, but enforce nothing at compile time, unlike Java's throws clause
- catch (...) when (condition) — exception filters with no direct Java equivalent
- using (including C# 8's more concise using declaration) is C#'s IDisposable-based answer to try-with-resources
- Next chapter: collections and a first taste of LINQ, previewing Course 2's own deep dive