Relevant link
Github solution: exercism-solutions
Exercism #47: Instrument of Texas
Learning Goal
Exception filtering | User defined exceptions
Notes
If you’ve read my recent posts, you should have noticed that I try to document task by task what I do for each exercise. I’ve decided to change that. To see my solution click on the link above to ge directly to the exercise solution on github.
I will use the posts now to gather information about the knowledge required to solve the problem but without reference to it. I hope yo like the new format, let’s get going
Exceptions Filtering
Core Concept
- Introduced in C# 6.0
- Lets you attach a boolean cndition (when (condition)) to a catch block
- The catch block only executes if the exception type matches AND the condition evaluates to true.
Syntax and structure

Why not just use if / throw inside catch?
| Feature | catch (Exception ex) when (cond) | catch (Exception ex) {if (!cond) throw; } |
| Stack Unwiding | Do not unwind stack if condition is false | Unwinds immediately upon entering catch. |
| Debug Info | Preserves full original call stack & memory state in crash dumps | Modifies call stack history(points to rethrow site) |
| Catch Scope | Skips straight to next handler if condition fails | Enters block, then forces expensive rethrow. |
Crucial rules and mechanics
- Two-pass execution model:
- Pass 1: The CLR searches the call stack evaluating filter expressions.
- Pass 2: Once a matching handler is found, the runtime unwinds the stack and executes finally and catch blocks.
- Side-effect logging pattern:
- You can call a method in the when clause that logs the error and returns false.
- Allows non-invasive logging without ever handling or intercepting the exception.
- Exceptions inside the filter:
- If your when(…) condition itself throws an exception, the CLR catches it silently, treats the filter as false, and keeps searching. It won’t crash your app.
Custom / User defined exceptions
Core concept
- Created by inheriting from System.Exception (or a more specific base like ArgumentException).
- Used when built-in exceptions (ArgumentNullException, InvalidOperationException, etc.) cannot clearly express domain-specific errors(e.g/, InsufficientFundsException, InventoryExhaustedException)).
Standard Patterns & Boilerplate
A complete, standard custom exception implements the three core contructors and any custom context properties:

Modern C#(.NET 8+) updates
- Legacy serialization (Obsolete): The [Serializable] attribute and protected (SerializationInfo, streamingContext) constructor are decrecated/obsolete in modern .NET(.NET8+). Do not implement unless maintinning legacy .NET framework code
- Primary contructor(C# 12+): You can write lightweight custom exceptions quickly:

Best practices checklist
- Naming: Always end the class name with the Exception suffix(e.g., PaymentFailedException).
- Inheritance target:
- Derive from Exception, never from ApplicatinException (Microsoft declared ApplicationException obsolete in practice).
- Derive from ArgumentException or HttpRequestException only if building a specialized subtype of that failure mode.
- Rich diagnostics: Add read-only properties for domain-specific state(e.g., OrderId, UserId) so catch blocks can inspect them without parsing string messages.
- Preserve Inner Exceptions: When catching a low-level error and re-throwing a custom domain exception, alwaays pass the original exception to the innerException parameter to preserve root-cause stack traces.
When not to create custom exceptions
| Situation | Don’t use custom | Use built-in instead |
| Bad method argument | CustomInvalidArgException | ArgumentException / ArgumentOutOfangeException |
| null passed inappropriately | CustomNullRefException | ArgumentNullException |
| Invalid object state | BadStateCustomException | InvalidOperationException |
| Method not build yet | CustomNotDoneException | NotImplementedException |
Related post: Mastering constructor chaining and custom exceptions