From a2009b82a403a6f0bdf96b904f5ce415723a4e1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Jarmu=C5=BC?= <33833009+bartosz-jarmuz@users.noreply.github.com> Date: Tue, 23 Nov 2021 13:09:38 +0100 Subject: [PATCH] Added sample code for throw vs throw ex (#24) --- .../ExceptionHandling/ExceptionHandling.sln | 25 ++++++ .../ThrowVsThrowEx/ThrowVsThrowEx.csproj | 18 +++++ .../ThrowVsThrowEx/ThrowVsThrowExSamples.cs | 78 +++++++++++++++++++ .../ThrowVsThrowEx/WorkerClasses.cs | 75 ++++++++++++++++++ 4 files changed, 196 insertions(+) create mode 100644 csharp-advanced-topics/ExceptionHandling/ExceptionHandling.sln create mode 100644 csharp-advanced-topics/ExceptionHandling/ThrowVsThrowEx/ThrowVsThrowEx.csproj create mode 100644 csharp-advanced-topics/ExceptionHandling/ThrowVsThrowEx/ThrowVsThrowExSamples.cs create mode 100644 csharp-advanced-topics/ExceptionHandling/ThrowVsThrowEx/WorkerClasses.cs diff --git a/csharp-advanced-topics/ExceptionHandling/ExceptionHandling.sln b/csharp-advanced-topics/ExceptionHandling/ExceptionHandling.sln new file mode 100644 index 0000000000..4310b11094 --- /dev/null +++ b/csharp-advanced-topics/ExceptionHandling/ExceptionHandling.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThrowVsThrowEx", "ThrowVsThrowEx\ThrowVsThrowEx.csproj", "{7E86B846-BE84-4BAA-BC9A-56872F6D9EF9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7E86B846-BE84-4BAA-BC9A-56872F6D9EF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7E86B846-BE84-4BAA-BC9A-56872F6D9EF9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7E86B846-BE84-4BAA-BC9A-56872F6D9EF9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7E86B846-BE84-4BAA-BC9A-56872F6D9EF9}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BCFF6A56-AED0-439E-A464-8FB6242B29C3} + EndGlobalSection +EndGlobal diff --git a/csharp-advanced-topics/ExceptionHandling/ThrowVsThrowEx/ThrowVsThrowEx.csproj b/csharp-advanced-topics/ExceptionHandling/ThrowVsThrowEx/ThrowVsThrowEx.csproj new file mode 100644 index 0000000000..c041c2184a --- /dev/null +++ b/csharp-advanced-topics/ExceptionHandling/ThrowVsThrowEx/ThrowVsThrowEx.csproj @@ -0,0 +1,18 @@ + + + + net6.0 + enable + None + false + + + + + + + + + + + diff --git a/csharp-advanced-topics/ExceptionHandling/ThrowVsThrowEx/ThrowVsThrowExSamples.cs b/csharp-advanced-topics/ExceptionHandling/ThrowVsThrowEx/ThrowVsThrowExSamples.cs new file mode 100644 index 0000000000..cd06709d9b --- /dev/null +++ b/csharp-advanced-topics/ExceptionHandling/ThrowVsThrowEx/ThrowVsThrowExSamples.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.Metrics; +using System.Linq; +using System.Threading.Channels; +using NUnit.Framework; + +namespace ThrowVsThrowEx +{ + public class ThrowVsThrowExSamples + { + [Test] + public void ThrowBehaviour_KeepsProperStackTrace() + { + try + { + new BusinessWorker().Work_Throw(); + } + catch (Exception ex) + { + Assert.AreEqual( + @"System.InvalidOperationException: That's a nasty bug! + at ThrowVsThrowEx.ThirdPartyComponent.g__DoDangerousOperation|1_0() + at ThrowVsThrowEx.ThirdPartyComponent.GoDeeper() + at ThrowVsThrowEx.ThirdPartyComponent.DoInternalWork() + at ThrowVsThrowEx.BusinessWorker.Work_Throw() + at ThrowVsThrowEx.ThrowVsThrowExSamples.ThrowBehaviour_KeepsProperStackTrace()", + ex.ToString()); + + } + } + + [Test] + public void ThrowEx_DropsTheStackTrace() + { + try + { + new BusinessWorker().Work_ThrowEx(); + } + catch (Exception ex) + { + Assert.AreEqual( + @"System.InvalidOperationException: That's a nasty bug! + at ThrowVsThrowEx.BusinessWorker.Work_ThrowEx() + at ThrowVsThrowEx.ThrowVsThrowExSamples.ThrowEx_DropsTheStackTrace()", + ex.ToString()); + } + } + + [Test] + public void WrapAndThrowNewEx_KeepsTheStackTraceInTheInnerException() + { + try + { + new BusinessWorker().Work_WrapAndThrowNewEx(); + } + catch (Exception ex) + { + //the stack trace of the top level exception is short + Assert.AreEqual(@" at ThrowVsThrowEx.BusinessWorker.Work_WrapAndThrowNewEx() + at ThrowVsThrowEx.ThrowVsThrowExSamples.WrapAndThrowNewEx_KeepsTheStackTraceInTheInnerException()", ex.StackTrace); + //however, the actual exception and it's stack trace is visible within the ex.InnerException property + //the full exception string also reveals all the layers of inner exceptions + Assert.AreEqual( + @"ThrowVsThrowEx.BusinessWorker+BusinessException: I am a business domain wrapper for internal exceptions. + ---> System.InvalidOperationException: That's a nasty bug! + at ThrowVsThrowEx.ThirdPartyComponent.g__DoDangerousOperation|1_0() + at ThrowVsThrowEx.ThirdPartyComponent.GoDeeper() + at ThrowVsThrowEx.ThirdPartyComponent.DoInternalWork() + at ThrowVsThrowEx.BusinessWorker.Work_WrapAndThrowNewEx() + --- End of inner exception stack trace --- + at ThrowVsThrowEx.BusinessWorker.Work_WrapAndThrowNewEx() + at ThrowVsThrowEx.ThrowVsThrowExSamples.WrapAndThrowNewEx_KeepsTheStackTraceInTheInnerException()", + ex.ToString()); + } + } + } +} \ No newline at end of file diff --git a/csharp-advanced-topics/ExceptionHandling/ThrowVsThrowEx/WorkerClasses.cs b/csharp-advanced-topics/ExceptionHandling/ThrowVsThrowEx/WorkerClasses.cs new file mode 100644 index 0000000000..e565dc60bc --- /dev/null +++ b/csharp-advanced-topics/ExceptionHandling/ThrowVsThrowEx/WorkerClasses.cs @@ -0,0 +1,75 @@ +using System; + +namespace ThrowVsThrowEx +{ + public class BusinessWorker + { + public void Work_Throw() + { + try + { + //lots of other business logic all around... + new ThirdPartyComponent().DoInternalWork(); + } + catch (Exception ex) + { + //here we would handle 'ex' in the BusinessWorker (clean up resources, log state, call 911 etc.) + throw; + } + } + + public void Work_ThrowEx() + { + try + { + //lots of other business logic all around... + new ThirdPartyComponent().DoInternalWork(); + } + catch (Exception ex) + { + //here we would handle 'ex' in the BusinessWorker (clean up resources, log state, call 911 etc.) + throw ex; + } + } + + public void Work_WrapAndThrowNewEx() + { + try + { + //lots of other business logic all around... + new ThirdPartyComponent().DoInternalWork(); + } + catch (Exception ex) + { + //here we would handle 'ex' in the BusinessWorker (clean up resources, log state, call 911 etc.) + throw new BusinessException("I am a business domain wrapper for internal exceptions.", ex); + } + } + + class BusinessException : Exception + { + public BusinessException(string? message, Exception? innerException) : base(message, innerException) + { + } + } + + + } + + public class ThirdPartyComponent + { + public void DoInternalWork() + { + GoDeeper(); + } + + private void GoDeeper() + { + DoDangerousOperation(); + void DoDangerousOperation() + { + throw new InvalidOperationException("That's a nasty bug!"); + } + } + } +} \ No newline at end of file