Caution
I need to revisit this. I don't fully understand it and the information below might not be completely accurate. I need to test this more with https://sharplab.io/ and re-read the https://dunnhq.com/posts/2022/throw-helper/ article as well as revisit the throw helpers implementations in the dotnet runtime repo.
Im addition, I should do a benchmark test to see what are the performance improvements, if any, besides code size reduction.
The ThrowHelper
pattern has the benefits explained in Parameter Null Checking in C# 11 and the ThrowHelper pattern.
This pattern to optimize the IL generated by throwing exceptions is used throughout on the dotnet runtime repo. See:
- System.Memory ThrowHelper: see explanation on the header of the source file for more details on why this pattern is used. This seems to be the one that best explains the overall idea.
- CoreLib ThrowHelper.cs: see explanation on the header of the source file for more details on why this pattern is used.
- ArgumentNullException.
- ArgumentException.
In short:
Having a throw new in your methods can be inefficient. The inefficiency comes from the fact that a fair amount of assembly code is generated to throw the exception."
"... there is a pattern that can help both eliminate the extra assembly code instructions, and optimize the code so that registers are not set-up needlessly."
Note
The [MethodImpl(MethodImplOptions.NoInlining)]
attribute for the Create<Exception>
method is what avoids the extra exception setup assembly
instructions from being populated on the call sites that use this method. This attribute doesn't seem to be required when the method only contains a throw
statement as you can see on the ArgumentNullException and ArgumentException.
Sometimes I don't implement the full ThrowHelper
pattern because I want to throw the exception from a switch case
. If I had the ThrowHelper
throw the exception the switch case
doesn't know that and I still need to return something after.
However, I could still implement the ThrowHelper
pattern even if I wanted to throw the exceptions on a switch case
if my Throw
method returned the type I needed for my switch case
. For instance, if I was doing a switch expression
that returns a string
I could have a Throw
method that I could use on the switch case
like this:
public sealed class UnexpectedTypeException : Exception
{
internal UnexpectedTypeException(string message)
: base(message)
{
}
[DoesNotReturn]
internal static T Throw<T>(object value, [CallerArgumentExpression("value")] string expression = "")
{
var type = value.GetType();
var message = $"{expression} represented an unhandled type: '{type.Name}' in '{type.Namespace}'.";
throw CreateUnexpectedTypeException(message);
}
[MethodImpl(MethodImplOptions.NoInlining)]
internal static UnexpectedTypeException CreateUnexpectedTypeException(string message)
{
return new UnexpectedTypeException(message);
}
}
and use on a string
returning switch expression
like this:
var someString = someObject switch
{
<some switch cases>
_ => UnexpectedTypeException.Throw<string>(someObject),
}
I haven't done the above for places where I throw an exception on switch expressions
because I'm not sure I like the idea. More importantly, in the end the ThrowHelper
optimization is irrelevant for this app. I'm just using it in some places to try it out and understand it better.