Skip to content

Instantly share code, notes, and snippets.

@benaadams
Last active October 16, 2017 20:34
Show Gist options
  • Save benaadams/216ed9516dc4e67728fdef1a77574f96 to your computer and use it in GitHub Desktop.
Save benaadams/216ed9516dc4e67728fdef1a77574f96 to your computer and use it in GitHub Desktop.
Throw Helper example
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using Microsoft.CodeAnalysis.Text;
public class TextLine
{
private SourceText Text;
private int Start;
private int Length;
public TextLine(SourceText text, int start, int length)
{
ThrowHelper.IfNullOrInvalidRangeThrow(text, start, length);
Text = text;
Start = start;
Length = length;
}
}
internal static class ThrowHelper
{
// Force inline the if tests; they are in the ThrowHelper so they don't have to be constainly repeated
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IfNullOrInvalidRangeThrow(SourceText text, int start, int length)
{
if (text == null || (uint) start > (uint) text.Length || (uint) length > (uint) (text.Length - start))
{
ThrowNullOrInvalidRangeException(text, start);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IfNullOrInvalidRangeThrow(string text, int start, int length)
{
if (text == null || (uint) start > (uint) text.Length || (uint) length > (uint) (text.Length - start))
{
ThrowNullOrInvalidRangeException(text, start);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IfNullOrInvalidRangeThrow<T>(T[] array, int start, int length)
{
if (array == null || (uint) start > (uint) array.Length || (uint) length > (uint) (array.Length - start))
{
ThrowNullOrInvalidRangeException(array, start);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IfNullOrInvalidRangeThrow<T, TCol>(TCol collection, int start, int length)
where TCol : ICollection<T>
{
if (collection == null)
{
ThrowArgumentNullException(ExceptionArgument.collection);
}
else if ((uint) start > (uint) collection.Count || (uint) length > (uint) (collection.Count - start))
{
ThrowInvalidRangeException(collection.Count, start);
}
}
// .NET Core & 4.7.1+ won't inline void methods that only throw (no other logic)
// which is what we want; also as it knows they throw is will skip stack unwinding etc after call
// So only get the exception and throw; defer the logic into the Get methods
public static void ThrowArgumentNullException(ExceptionArgument argument)
{
throw GetArgumentNullException(argument);
}
// Throws for null/invalid ranges
public static void ThrowNullOrInvalidRangeException(SourceText text, int start)
{
throw GetCtorValidationException(text, start);
}
public static void ThrowNullOrInvalidRangeException(string text, int start)
{
throw GetCtorValidationException(text, start);
}
public static void ThrowNullOrInvalidRangeException(Array array, int start)
{
throw GetCtorValidationException(array, start);
}
public static void ThrowInvalidRangeException(int count, int start)
{
throw GetCtorValidationException(count, start);
}
// .NET Core & 4.7.1+ won't inline void methods that only throw (no other logic)
// However older runtimes may, so add a NoInlining for the GetException methods
[MethodImpl(MethodImplOptions.NoInlining)]
private static Exception GetArgumentNullException(ExceptionArgument argument)
{
return new ArgumentNullException(GetArgumentName(argument));
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static Exception GetArgumentOutOfRangeException(ExceptionArgument argument)
{
return new ArgumentOutOfRangeException(GetArgumentName(argument));
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static Exception GetCtorValidationException(SourceText text, int start)
{
if (text == null)
{
return GetArgumentNullException(ExceptionArgument.text);
}
return GetCtorValidationException(text.Length, start);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static Exception GetCtorValidationException(string text, int start)
{
if (text == null)
{
return GetArgumentNullException(ExceptionArgument.text);
}
return GetCtorValidationException(text.Length, start);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static Exception GetCtorValidationException(Array array, int start)
{
if (array == null)
{
return GetArgumentNullException(ExceptionArgument.array);
}
return GetCtorValidationException(array.Length, start);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static Exception GetCtorValidationException(int count, int start)
{
if ((uint) start > (uint) count)
{
return GetArgumentOutOfRangeException(ExceptionArgument.start);
}
return GetArgumentOutOfRangeException(ExceptionArgument.length);
}
// We use ExceptionArgument/ExceptionResource enums rather than nameof or string values
// as numeric consts are more lightweight to pass than string consts (no string load)
// this pushes the asm for string lookup out of the instruction cache of the fast non-throwing path
// This function will convert an ExceptionArgument enum value to the argument string.
[MethodImpl(MethodImplOptions.NoInlining)]
private static string GetArgumentName(ExceptionArgument argument)
{
Debug.Assert(Enum.IsDefined(typeof(ExceptionArgument), argument),
"The enum value is not defined, please check the ExceptionArgument Enum.");
return argument.ToString();
}
// This function will convert an ExceptionResource enum value to the resource string.
[MethodImpl(MethodImplOptions.NoInlining)]
private static string GetResourceString(ExceptionResource resource)
{
Debug.Assert(Enum.IsDefined(typeof(ExceptionResource), resource),
"The enum value is not defined, please check the ExceptionResource Enum.");
return SR.GetResourceString(resource.ToString());
}
}
// Resource lookup class
internal static class SR
{
internal static string GetResourceString(string resourceKey) => GetResourceString(resourceKey, String.Empty);
internal static string GetResourceString(string resourceKey, string defaultString)
{
// Do proper resource lookup
return defaultString ?? resourceKey;
}
}
// The convention for this enum is using the argument name as the enum name
internal enum ExceptionArgument
{
text,
collection,
start,
length,
array
}
// Resource lookup strings
internal enum ExceptionResource
{
ArgumentOutOfRange_NeedNonNegNum
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment