[{"data":1,"prerenderedAt":115},["ShallowReactive",2],{"qa-\u002Fdotnet\u002Fcsharp-core\u002Fexceptions":3},{"page":4,"siblings":95,"blog":112},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"questionsCount":85,"related":86,"seo":87,"seoDescription":88,"stem":89,"subtopic":90,"topic":91,"topicSlug":92,"updated":93,"__hash__":94},"qa\u002Fdotnet\u002Fcsharp-core\u002Fexceptions.md","Exceptions",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md",".NET Core","dotnet",{},true,5,"\u002Fdotnet\u002Fcsharp-core\u002Fexceptions",[23,28,32,36,40,44,48,53,57,61,65,69,73,77,81],{"id":24,"difficulty":25,"q":26,"a":27},"exception-hierarchy","easy","What is the .NET exception hierarchy and how does it relate to what you should catch?","All exceptions in .NET inherit from `System.Exception`. The hierarchy has two main\nbranches: **SystemException** (runtime conditions, usually unrecoverable) and\n**ApplicationException** (deprecated base for user-defined exceptions — not used in\nmodern code).\n\n```csharp\n\u002F\u002F Simplified hierarchy:\n\u002F\u002F Exception\n\u002F\u002F SystemException\n\u002F\u002F NullReferenceException\n\u002F\u002F InvalidOperationException\n\u002F\u002F ArgumentException\n\u002F\u002F ArgumentNullException\n\u002F\u002F ArgumentOutOfRangeException\n\u002F\u002F IndexOutOfRangeException\n\u002F\u002F IOException\n\u002F\u002F FileNotFoundException\n\u002F\u002F ArithmeticException\n\u002F\u002F DivideByZeroException\n\u002F\u002F OutOfMemoryException      ← usually unrecoverable\n\u002F\u002F StackOverflowException    ← process terminates\n\u002F\u002F ApplicationException        ← deprecated, avoid\n\n\u002F\u002F Catch the most specific type you can handle:\ntry\n{\n    File.ReadAllText(\"data.txt\");\n}\ncatch (FileNotFoundException ex)\n{\n    Console.WriteLine($\"File missing: {ex.FileName}\");\n}\ncatch (IOException ex)\n{\n    Console.WriteLine($\"I\u002FO error: {ex.Message}\");\n}\ncatch (Exception ex)\n{\n    \u002F\u002F Last resort — log and rethrow, or rethrow as-is\n    logger.LogError(ex, \"Unexpected error\");\n    throw;\n}\n```\n\n**Rule of thumb:** Catch the *most specific* exception type you can meaningfully\nhandle. Avoid catching `Exception` unless you log and rethrow. Never catch\n`OutOfMemoryException` or `StackOverflowException` — the process state is corrupt.\n",{"id":29,"difficulty":14,"q":30,"a":31},"throw-vs-throw-ex","What is the difference between `throw` and `throw ex` and why does it matter?","`throw` (bare) **re-throws the current exception**, preserving the original stack\ntrace. `throw ex` **throws the caught exception as if it were new**, replacing the\nstack trace with the current location — destroying information that is critical for\ndebugging.\n\n```csharp\nvoid Outer()\n{\n    try { Inner(); }\n    catch (Exception ex) { throw ex; } \u002F\u002F BAD — loses Inner's stack trace\n}\n\nvoid Inner()\n{\n    throw new InvalidOperationException(\"failed here\");\n}\n\n\u002F\u002F With 'throw ex': stack trace points to Outer (the catch block)\n\u002F\u002F With 'throw':    stack trace points to Inner (where it actually failed)\n\n\u002F\u002F Correct: preserve the original stack trace\nvoid OuterCorrect()\n{\n    try { Inner(); }\n    catch (Exception ex)\n    {\n        logger.LogError(ex, \"Error in Inner\");\n        throw; \u002F\u002F re-throw — stack trace unchanged\n    }\n}\n\n\u002F\u002F Wrapping exceptions — preserve original as InnerException:\nvoid OuterWrapped()\n{\n    try { Inner(); }\n    catch (InvalidOperationException ex)\n    {\n        throw new ApplicationException(\"High-level failure\", ex); \u002F\u002F ex = InnerException\n    }\n}\n\u002F\u002F Both the original and wrapping exceptions are in the chain\n```\n\n**Rule of thumb:** Always use bare `throw` to re-throw. If you need to wrap an\nexception in a higher-level one, pass the original as `innerException`. Examine the\nstack trace whenever you see `throw ex` in code review — it is almost always a bug.\n",{"id":33,"difficulty":25,"q":34,"a":35},"finally-block","What does `finally` guarantee and when is it NOT executed?","The `finally` block always runs after `try`\u002F`catch`, **whether or not** an exception\nwas thrown, and **whether or not** the exception was handled. It is used for cleanup\n(closing files, releasing locks, etc.).\n\n```csharp\nFileStream? fs = null;\ntry\n{\n    fs = File.OpenRead(\"data.txt\");\n    Process(fs);\n}\ncatch (IOException ex)\n{\n    Console.WriteLine(ex.Message);\n}\nfinally\n{\n    fs?.Close(); \u002F\u002F always runs — even if Process throws, even if catch throws\n    Console.WriteLine(\"Cleaned up\");\n}\n\n\u002F\u002F finally does NOT run in these cases:\n\u002F\u002F 1. StackOverflowException — the CLR tears down the process\n\u002F\u002F 2. Environment.FailFast() — immediate process termination\n\u002F\u002F 3. Infinite loop or deadlock before reaching finally\n\n\u002F\u002F Modern preference: 'using' statement replaces try\u002Ffinally for IDisposable:\nusing var stream = File.OpenRead(\"data.txt\"); \u002F\u002F Dispose called automatically\nProcess(stream);\n\u002F\u002F No explicit finally needed — cleaner and exception-safe\n```\n\n**Rule of thumb:** Use `using` \u002F `using var` for `IDisposable` objects instead of\nmanual `try\u002Ffinally` — it generates the same IL but is far more readable. Reserve\nexplicit `finally` for cleanup that isn't covered by `IDisposable`.\n",{"id":37,"difficulty":14,"q":38,"a":39},"custom-exceptions","How do you create a well-designed custom exception class?","A custom exception should inherit from `Exception` (not `ApplicationException`),\nprovide the standard constructors, and include domain-specific context.\n\n```csharp\n\u002F\u002F Well-designed custom exception:\npublic class OrderValidationException : Exception\n{\n    public int OrderId { get; }\n    public string Field { get; }\n\n    \u002F\u002F Standard constructors — needed for serialisation and chaining:\n    public OrderValidationException() { }\n\n    public OrderValidationException(string message)\n        : base(message) { }\n\n    public OrderValidationException(string message, Exception innerException)\n        : base(message, innerException) { }\n\n    \u002F\u002F Domain-specific constructor:\n    public OrderValidationException(int orderId, string field, string message)\n        : base(message)\n    {\n        OrderId = orderId;\n        Field   = field;\n    }\n}\n\n\u002F\u002F Usage:\nvoid ValidateOrder(Order order)\n{\n    if (order.Total \u003C 0)\n        throw new OrderValidationException(order.Id, nameof(order.Total),\n            $\"Order {order.Id}: Total cannot be negative.\");\n}\n\ntry\n{\n    ValidateOrder(bad);\n}\ncatch (OrderValidationException ex)\n{\n    logger.LogWarning(\"Validation failed for order {OrderId}, field {Field}: {Message}\",\n        ex.OrderId, ex.Field, ex.Message);\n}\n```\n\n**Rule of thumb:** Create custom exceptions when callers need to distinguish your\nerror from others programmatically (i.e., they would catch it specifically). Add\nproperties for context that logs and handlers need. Do not add custom exceptions just\nfor error messages — `InvalidOperationException` with a good message is often enough.\n",{"id":41,"difficulty":14,"q":42,"a":43},"aggregate-exception","What is `AggregateException` and when does it appear?","`AggregateException` wraps one or more inner exceptions. The TPL (Task Parallel\nLibrary) and `Task.WhenAll` use it to collect multiple failures that happened\nsimultaneously.\n\n```csharp\nvar t1 = Task.Run(() => throw new InvalidOperationException(\"err1\"));\nvar t2 = Task.Run(() => throw new ArgumentException(\"err2\"));\n\ntry\n{\n    await Task.WhenAll(t1, t2);\n}\ncatch (Exception ex)\n{\n    \u002F\u002F await re-throws only the FIRST inner exception:\n    Console.WriteLine(ex.GetType().Name);  \u002F\u002F InvalidOperationException\n}\n\n\u002F\u002F To inspect ALL exceptions:\nvar tasks = new[] { t1, t2 };\ntry { await Task.WhenAll(tasks); }\ncatch { }\nforeach (var t in tasks.Where(t => t.IsFaulted))\n{\n    AggregateException? agg = t.Exception;\n    foreach (var inner in agg!.InnerExceptions)\n        Console.WriteLine(inner.Message); \u002F\u002F err1, then err2\n}\n\n\u002F\u002F Parallel.ForEach also throws AggregateException:\ntry\n{\n    Parallel.ForEach(items, item => ProcessItem(item));\n}\ncatch (AggregateException agg)\n{\n    \u002F\u002F Handle or rethrow individual exceptions:\n    agg.Handle(ex =>\n    {\n        if (ex is TimeoutException) { logger.LogWarning(ex, \"Timeout\"); return true; }\n        return false; \u002F\u002F unhandled — re-thrown\n    });\n}\n```\n\n**Rule of thumb:** When awaiting a `Task.WhenAll`, inspect the individual Task\n`.Exception` properties if you need to see every failure. The single caught exception\nfrom `await` only surfaces the first one.\n",{"id":45,"difficulty":14,"q":46,"a":47},"exception-filters","What is a `catch` block `when` filter and how does it help?","A `when` clause on a `catch` block adds a boolean condition. If it evaluates to\n`false`, the catch is **skipped** (as if it didn't exist) and the stack is not\nunwound — other handlers further up the call stack can catch it instead.\n\n```csharp\n\u002F\u002F Catch only specific HTTP status codes:\ntry\n{\n    var response = await client.GetAsync(url);\n    response.EnsureSuccessStatusCode();\n}\ncatch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.NotFound)\n{\n    return null; \u002F\u002F 404 — treat as \"not found\", not an error\n}\ncatch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.Unauthorized)\n{\n    throw new UnauthorizedException(\"Token expired\", ex);\n}\n\u002F\u002F Other HttpRequestExceptions propagate normally\n\n\u002F\u002F Logging without catching — the filter runs BEFORE the stack unwinds:\ncatch (Exception ex) when (Log(ex)) { } \u002F\u002F Log() always returns false — just a side effect\n\nbool Log(Exception ex)\n{\n    logger.LogError(ex, \"Unhandled exception\");\n    return false; \u002F\u002F returning false means the filter doesn't match; exception propagates\n}\n\n\u002F\u002F Distinguish transient vs permanent errors:\ncatch (SqlException ex) when (IsTransient(ex))\n{\n    await RetryAsync();\n}\n```\n\n**Rule of thumb:** Use `when` filters to narrow catch scope without losing the\noriginal exception or unwinding the stack prematurely. Use the false-returning filter\ntrick for \"log and rethrow\" without changing stack trace.\n",{"id":49,"difficulty":50,"q":51,"a":52},"exception-dispatch-info","hard","What is `ExceptionDispatchInfo` and when do you use it?","`ExceptionDispatchInfo` captures an exception — including its stack trace — and\nallows you to re-throw it later, **preserving the original stack trace**, even from\na different thread or context. It is what `await` uses internally to propagate\nexceptions from Tasks.\n\n```csharp\nExceptionDispatchInfo? captured = null;\n\nThread t = new Thread(() =>\n{\n    try { throw new InvalidOperationException(\"from thread\"); }\n    catch (Exception ex)\n    {\n        \u002F\u002F Capture preserves the original stack trace:\n        captured = ExceptionDispatchInfo.Capture(ex);\n    }\n});\nt.Start();\nt.Join();\n\n\u002F\u002F Re-throw on the calling thread — original stack trace is preserved!\ncaptured?.Throw();\n\u002F\u002F Stack trace shows the original thread's call stack, not this point\n\n\u002F\u002F Real use case: manual async exception propagation\nExceptionDispatchInfo? error = null;\nvar task = Task.Run(() =>\n{\n    try { RiskyWork(); }\n    catch (Exception ex) { error = ExceptionDispatchInfo.Capture(ex); }\n});\nawait task;\nerror?.Throw(); \u002F\u002F same as 'await task' but explicit\n```\n\n`ExceptionDispatchInfo` also exposes `.SourceException` to inspect the captured\nexception without re-throwing.\n\n**Rule of thumb:** You rarely need `ExceptionDispatchInfo` directly — `async\u002Fawait`\nhandles it for you. Use it when marshalling exceptions between threads manually,\nor building custom async\u002Fsync bridges.\n",{"id":54,"difficulty":14,"q":55,"a":56},"global-exception-handler","How do you handle unhandled exceptions globally in an ASP.NET Core application?","ASP.NET Core provides multiple mechanisms for global exception handling, ranging\nfrom simple middleware to fine-grained problem-details responses.\n\n```csharp\n\u002F\u002F 1. UseExceptionHandler middleware (built-in):\napp.UseExceptionHandler(errorApp =>\n{\n    errorApp.Run(async context =>\n    {\n        var feature = context.Features.Get\u003CIExceptionHandlerFeature>();\n        var ex = feature?.Error;\n        context.Response.StatusCode = 500;\n        context.Response.ContentType = \"application\u002Fjson\";\n        await context.Response.WriteAsJsonAsync(new { error = ex?.Message });\n    });\n});\n\n\u002F\u002F 2. IExceptionHandler interface (.NET 8+) — preferred:\npublic class GlobalExceptionHandler : IExceptionHandler\n{\n    private readonly ILogger\u003CGlobalExceptionHandler> _logger;\n    public GlobalExceptionHandler(ILogger\u003CGlobalExceptionHandler> logger)\n        => _logger = logger;\n\n    public async ValueTask\u003Cbool> TryHandleAsync(\n        HttpContext context, Exception exception, CancellationToken ct)\n    {\n        _logger.LogError(exception, \"Unhandled exception\");\n\n        var statusCode = exception switch\n        {\n            NotFoundException => StatusCodes.Status404NotFound,\n            UnauthorizedException => StatusCodes.Status401Unauthorized,\n            _ => StatusCodes.Status500InternalServerError\n        };\n\n        context.Response.StatusCode = statusCode;\n        await context.Response.WriteAsJsonAsync(\n            new ProblemDetails { Title = exception.Message, Status = statusCode }, ct);\n\n        return true; \u002F\u002F exception handled\n    }\n}\n\n\u002F\u002F Register:\nbuilder.Services.AddExceptionHandler\u003CGlobalExceptionHandler>();\nbuilder.Services.AddProblemDetails();\napp.UseExceptionHandler();\n```\n\n**Rule of thumb:** Use `.NET 8` `IExceptionHandler` for structured, testable global\nerror handling. Map domain exceptions (NotFoundException, ValidationException) to\nappropriate HTTP status codes here rather than scattering status code decisions\nacross controllers.\n",{"id":58,"difficulty":14,"q":59,"a":60},"exception-rethrow-patterns","What are the common patterns for rethrowing exceptions and when should each be used?","There are four ways to rethrow or propagate an exception, each with different semantics.\n\n```csharp\ntry { DoWork(); }\n\n\u002F\u002F 1. bare throw — preserve original exception and stack trace (almost always correct)\ncatch (Exception ex) { logger.LogError(ex, \"...\"); throw; }\n\n\u002F\u002F 2. throw new — wrap the original as InnerException; adds context, changes type\ncatch (SqlException ex)\n    throw new RepositoryException(\"DB operation failed\", ex);  \u002F\u002F inner preserved\n\n\u002F\u002F 3. throw ex — AVOID: re-throws same exception but clears stack trace\ncatch (Exception ex) { throw ex; } \u002F\u002F stack trace is gone\n\n\u002F\u002F 4. ExceptionDispatchInfo.Capture — capture + rethrow across thread\u002Fcontext boundary\nExceptionDispatchInfo? captured = null;\nawait Task.Run(() =>\n{\n    try { RiskyWork(); }\n    catch (Exception ex) { captured = ExceptionDispatchInfo.Capture(ex); }\n});\ncaptured?.Throw(); \u002F\u002F same exception, original stack trace, different thread\n\n\u002F\u002F Pattern comparison:\n\u002F\u002F bare throw     — same exception, same stack trace, same type\n\u002F\u002F throw new(inner) — new exception type, InnerException = original, new stack trace\n\u002F\u002F ExceptionDispatchInfo — same exception, original stack trace, across threads\n\n\u002F\u002F When to wrap:\nvoid LoadConfig()\n{\n    try { File.ReadAllText(\"config.json\"); }\n    catch (IOException ex)\n        \u002F\u002F translate low-level exception to domain exception:\n        throw new ConfigurationException(\"Cannot load configuration\", ex);\n}\n```\n\n**Rule of thumb:** Default to bare `throw`. Wrap with a new exception type when you\nwant to translate a low-level technical exception into a domain-meaningful one —\nalways pass the original as `innerException`. Only use `ExceptionDispatchInfo` when\ncrossing thread boundaries manually.\n",{"id":62,"difficulty":14,"q":63,"a":64},"checked-unchecked","What are `checked` and `unchecked` contexts for arithmetic overflow?","By default, C# arithmetic is **unchecked** — integer overflow silently wraps around.\nThe `checked` keyword \u002F block makes overflow throw `OverflowException`.\n\n```csharp\nint max = int.MaxValue; \u002F\u002F 2,147,483,647\n\n\u002F\u002F Unchecked (default) — wraps silently:\nint overflow = max + 1; \u002F\u002F -2,147,483,648 — no exception!\n\n\u002F\u002F Checked — throws OverflowException:\ntry\n{\n    int safe = checked(max + 1);\n}\ncatch (OverflowException)\n{\n    Console.WriteLine(\"Overflow detected\");\n}\n\n\u002F\u002F Checked block for multiple expressions:\nchecked\n{\n    int a = int.MaxValue;\n    int b = a + 1; \u002F\u002F throws\n}\n\n\u002F\u002F unchecked block — explicitly opt out in a checked project:\nunchecked\n{\n    int wrapped = int.MaxValue + 1; \u002F\u002F -2,147,483,648 — intentional wrap\n}\n\n\u002F\u002F Real use: computing array indices, hash codes, byte conversions:\n\u002F\u002F Hash code computation often intentionally overflows:\nunchecked\n{\n    int hash = 17;\n    hash = hash * 31 + value.GetHashCode(); \u002F\u002F overflow is fine here\n    return hash;\n}\n```\n\nC# projects can be compiled with `\u002Fchecked+` to enable checked arithmetic globally.\nMost .NET projects don't, favouring performance. `decimal` arithmetic is always\nchecked. Floating-point (`float`, `double`) uses IEEE 754 rules — no OverflowException.\n\n**Rule of thumb:** Use `checked` when computing values that feed into array indices,\ncounts, or financial calculations where silent overflow would be a bug. Use `unchecked`\nexplicitly in hash code computations where overflow is intentional.\n",{"id":66,"difficulty":14,"q":67,"a":68},"exception-best-practices","What are the key do's and don'ts of exception handling in production code?","Exception handling mistakes compound in production — swallowed exceptions,\ndestroyed stack traces, and overly broad catches all make incidents harder to resolve.\n\n```csharp\n\u002F\u002F DON'T: swallow exceptions silently\ntry { DoWork(); }\ncatch (Exception) { } \u002F\u002F exception disappears — impossible to debug\n\n\u002F\u002F DO: log and rethrow, or handle specifically\ntry { DoWork(); }\ncatch (Exception ex)\n{\n    logger.LogError(ex, \"DoWork failed\");\n    throw; \u002F\u002F preserve stack trace\n}\n\n\u002F\u002F DON'T: throw ex (destroys stack trace)\ncatch (Exception ex) { throw ex; }\n\n\u002F\u002F DO: bare throw\ncatch (Exception ex) { logger.LogError(ex, \"...\"); throw; }\n\n\u002F\u002F DON'T: use exceptions for control flow in hot paths\ntry { return int.Parse(input); }\ncatch { return 0; } \u002F\u002F exceptions are slow for expected failure\n\n\u002F\u002F DO: use TryParse for expected failure\nint.TryParse(input, out int result); \u002F\u002F returns false, no exception\n\n\u002F\u002F DON'T: catch Exception in library code unless you rethrow\npublic int Calculate(string input)\n{\n    try { return int.Parse(input); }\n    catch (Exception) { return -1; } \u002F\u002F caller doesn't know WHY it failed\n}\n\n\u002F\u002F DO: let exceptions propagate; only catch what you can handle\npublic int Calculate(string input) => int.Parse(input); \u002F\u002F let FormatException surface\n\n\u002F\u002F DO: validate input at boundaries to avoid exceptions in the first place\npublic void SetQuantity(int qty)\n{\n    ArgumentOutOfRangeException.ThrowIfNegativeOrZero(qty);\n    _quantity = qty;\n}\n```\n\n**Rule of thumb:** Exceptions should be *exceptional* — unexpected failures, not\nexpected control flow. Always log before rethrowing. Use `TryXxx` patterns for\nexpected failure cases. Validate inputs at method boundaries to prevent exceptions\nfrom propagating unnecessarily.\n",{"id":70,"difficulty":25,"q":71,"a":72},"throw-expression","What is a throw expression (C# 7) and how does it improve code clarity?","A **throw expression** (C# 7) allows `throw` to appear in expression contexts —\nternary operators, null-coalescing operators, and expression-bodied members — where\npreviously only `throw` *statements* were allowed.\n\n```csharp\n\u002F\u002F Null-coalescing throw (very common pattern):\npublic class OrderService\n{\n    private readonly IOrderRepository _repo;\n\n    \u002F\u002F Old style — required extra if statement:\n    public OrderService(IOrderRepository repo)\n    {\n        if (repo == null) throw new ArgumentNullException(nameof(repo));\n        _repo = repo;\n    }\n\n    \u002F\u002F C# 7 throw expression — single line:\n    public OrderService(IOrderRepository repo)\n        => _repo = repo ?? throw new ArgumentNullException(nameof(repo));\n}\n\n\u002F\u002F In ternary operator:\nstring GetName(Customer? c)\n    => c != null ? c.Name : throw new ArgumentNullException(nameof(c));\n\n\u002F\u002F In expression-bodied property:\nprivate string _name = \"\";\npublic string Name\n{\n    get => _name;\n    set => _name = value?.Trim() ?? throw new ArgumentNullException(nameof(value));\n}\n\n\u002F\u002F ArgumentNullException.ThrowIfNull (.NET 6+) — even cleaner:\npublic OrderService(IOrderRepository repo)\n{\n    ArgumentNullException.ThrowIfNull(repo);\n    _repo = repo;\n}\n```\n\n**Rule of thumb:** Use `?? throw new ArgumentNullException(nameof(param))` for\nnull-guard assignments. In .NET 6+, prefer `ArgumentNullException.ThrowIfNull(param)`\nand `ArgumentException.ThrowIfNullOrEmpty(param)` in method bodies — they are\nmore readable and generate slightly better IL.\n",{"id":74,"difficulty":50,"q":75,"a":76},"exception-performance","Why are exceptions expensive in .NET and how do you avoid them on hot paths?","Throwing an exception is expensive because the CLR must capture the **stack trace**\n(walking the call stack, allocating strings for each frame) and allocate the exception\nobject on the heap. In hot-path code this cost is significant.\n\n```csharp\n\u002F\u002F Expensive — exception used for expected failure (parsing user input):\nint ParseBad(string input)\n{\n    try { return int.Parse(input); }\n    catch (FormatException) { return -1; } \u002F\u002F allocates exception, walks stack\n}\n\n\u002F\u002F Cheap — TryParse avoids exception entirely:\nint ParseGood(string input)\n    => int.TryParse(input, out int result) ? result : -1; \u002F\u002F no exception, no allocation\n\n\u002F\u002F Pattern: Try-method convention for expected failure:\npublic bool TryResolve(string key, out string? value)\n{\n    \u002F\u002F returns false on failure — caller decides how to handle it\n    return _cache.TryGetValue(key, out value);\n}\n\n\u002F\u002F Result\u003CT> pattern (alternative to exceptions for expected errors):\npublic record Result\u003CT>(T? Value, string? Error, bool IsSuccess);\n\nResult\u003COrder> LoadOrder(int id)\n{\n    var order = _repo.Find(id);\n    return order is null\n        ? new Result\u003COrder>(null, $\"Order {id} not found\", false)\n        : new Result\u003COrder>(order, null, true);\n}\n\nvar result = LoadOrder(42);\nif (!result.IsSuccess) Console.WriteLine(result.Error);\n\n\u002F\u002F Note: exceptions are ONLY expensive when thrown.\n\u002F\u002F try\u002Fcatch blocks with no exception have near-zero overhead on modern JIT.\n```\n\n**Rule of thumb:** Reserve exceptions for **unexpected**, *exceptional* failures —\nnot for expected outcomes like \"user typed bad input\" or \"record not found.\" For\nexpected failures, use the `TryXxx` pattern or a result type. Profile before assuming\nexceptions are your bottleneck; the overhead only materialises when they are actually thrown.\n",{"id":78,"difficulty":25,"q":79,"a":80},"inner-exception","What is `InnerException` and how do you use exception chaining correctly?","When you catch an exception and throw a new one, the original exception should be\npassed as the **inner exception** — preserving the root cause while providing\nhigher-level context. `InnerException` forms a chain that can be walked to find\nthe original failure.\n\n```csharp\n\u002F\u002F Low-level method throws a technical exception:\nvoid ReadConfig(string path)\n{\n    \u002F\u002F Throws FileNotFoundException if missing\n    var text = File.ReadAllText(path);\n    \u002F\u002F Throws JsonException if malformed\n    JsonSerializer.Deserialize\u003CConfig>(text);\n}\n\n\u002F\u002F Mid-level method wraps and adds context:\nConfig LoadConfig(string path)\n{\n    try { ReadConfig(path); }\n    catch (FileNotFoundException ex)\n        throw new ConfigurationException($\"Config file not found: {path}\", ex);\n    catch (JsonException ex)\n        throw new ConfigurationException($\"Config file is malformed: {path}\", ex);\n    return new Config(); \u002F\u002F unreachable after throw\n}\n\n\u002F\u002F Caller walks the chain:\ntry { LoadConfig(\"appsettings.json\"); }\ncatch (ConfigurationException ex)\n{\n    Console.WriteLine($\"Error: {ex.Message}\");\n    Console.WriteLine($\"Caused by: {ex.InnerException?.Message}\");\n\n    \u002F\u002F Walk the full chain:\n    Exception? current = ex;\n    while (current != null)\n    {\n        Console.WriteLine($\"  {current.GetType().Name}: {current.Message}\");\n        current = current.InnerException;\n    }\n}\n\n\u002F\u002F Exception.GetBaseException() — returns the deepest InnerException:\nException root = ex.GetBaseException();\nConsole.WriteLine(root.Message); \u002F\u002F the original FileNotFoundException message\n```\n\n**Rule of thumb:** Always pass the original exception as `innerException` when\nwrapping. This preserves the complete failure chain for logging and debugging. Never\ndiscard the original exception — if you don't need to wrap it, use bare `throw` instead.\n",{"id":82,"difficulty":14,"q":83,"a":84},"validation-vs-exception","When should you use guard clauses and validation instead of relying on exceptions?","**Guard clauses** validate preconditions at the entry point of a method and throw\nimmediately — before any work begins. This produces clearer error messages, fails fast,\nand avoids propagating invalid state deep into business logic where the original cause\nis harder to diagnose.\n\n```csharp\n\u002F\u002F Without guard clauses — NullReferenceException buried somewhere in logic:\npublic decimal CalculateTotal(Order order)\n{\n    \u002F\u002F order might be null — crash happens inside the loop, far from the call site\n    return order.Items.Sum(i => i.Price * i.Quantity);\n}\n\n\u002F\u002F With guard clauses — fail immediately with a clear message:\npublic decimal CalculateTotal(Order order)\n{\n    ArgumentNullException.ThrowIfNull(order);            \u002F\u002F .NET 6+\n    ArgumentNullException.ThrowIfNull(order.Items);\n\n    return order.Items.Sum(i => i.Price * i.Quantity);\n}\n\n\u002F\u002F .NET 6+ guard helpers (prefer over manual checks):\nArgumentNullException.ThrowIfNull(value);\nArgumentException.ThrowIfNullOrEmpty(str);\nArgumentException.ThrowIfNullOrWhiteSpace(str);\nArgumentOutOfRangeException.ThrowIfNegative(count);\nArgumentOutOfRangeException.ThrowIfNegativeOrZero(count);\nArgumentOutOfRangeException.ThrowIfGreaterThan(index, max);\n\n\u002F\u002F For expected failure (not a programming error), prefer TryXxx or Result:\n\u002F\u002F Guard clause = programmer used the API wrong (throw ArgumentException)\n\u002F\u002F Expected failure = valid input that yields no result (return false or Result)\n\n\u002F\u002F Example: ID lookup is a valid scenario; bad argument is not\npublic bool TryGetUser(int id, out User? user)\n{\n    ArgumentOutOfRangeException.ThrowIfNegativeOrZero(id); \u002F\u002F guard: must be positive\n    user = _store.Find(id); \u002F\u002F null is a valid \"not found\" result\n    return user is not null;\n}\n```\n\n**Rule of thumb:** Validate all public method arguments with guard clauses and throw\n`ArgumentException` (or its subclasses) immediately. Distinguish between **programming\nerrors** (bad arguments — throw) and **expected absence** (not found — return false\nor null). Guard clauses at the top of a method act as executable documentation of\nits preconditions.\n",15,null,{"description":11},"C# exception handling interview questions — throw vs throw ex, stack trace preservation, custom exceptions, AggregateException, and global handlers.","dotnet\u002Fcsharp-core\u002Fexceptions","Exception Handling","C# Core","csharp-core","2026-06-23","zAF95309M5zOh-WrjO567m4WpuQd4mvaLkxRIySGCHA",[96,100,103,107,111],{"subtopic":97,"path":98,"order":99},"Async \u002F Await","\u002Fdotnet\u002Fcsharp-core\u002Fasync-await",1,{"subtopic":101,"path":102,"order":12},"Delegates & Events","\u002Fdotnet\u002Fcsharp-core\u002Fdelegates-events",{"subtopic":104,"path":105,"order":106},"Pattern Matching","\u002Fdotnet\u002Fcsharp-core\u002Fpattern-matching",3,{"subtopic":108,"path":109,"order":110},"Collections","\u002Fdotnet\u002Fcsharp-core\u002Fcollections",4,{"subtopic":90,"path":21,"order":20},{"path":113,"title":114},"\u002Fblog\u002Fdotnet-exception-handling-best-practices","Exception Handling Patterns in C#",1782244118051]