Skip to content

Latest commit

 

History

History
72 lines (55 loc) · 4.5 KB

throw-helper.md

File metadata and controls

72 lines (55 loc) · 4.5 KB

How to throw exceptions

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:

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.

ThrowHelper and switch expressions

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.