Last active
October 16, 2017 20:34
-
-
Save benaadams/216ed9516dc4e67728fdef1a77574f96 to your computer and use it in GitHub Desktop.
Throw Helper example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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