-
Notifications
You must be signed in to change notification settings - Fork 405
Description
There are currently two types extending the UnitsNetException (AmbigiousUnitParseException and the UnitNotFoundException) - with a bunch of standard exceptions that are scattered around (a lot of them part of the auto-generated code).
Besides the likely inconsistency in the messaging, having the same string format repeated in code is of course also increasing our file size.
Here is the list of exceptions that I think should cover all of our cases:
UnitNotFoundException: this one is pretty obvious, it is thrown by theUnitConverter/UnitParseretc - it is also the exception that I typically use when given an invalid unit, such as(MassUnit)(-1)QuantityNotFoundException: this is thrown when given atype(or type name) that is unknown- this one is thrown by theQuantityInfoLookup(which is itself called by theQuantity.Parseor theUnitConverter)InvalidConversionException: I'm not sure if this one applies to the current state of thev6, but in my new implementation of theUnitConverterthis is thrown when attempting to convert one quantity to another, incompatible, quantity typeAmbiguousUnitParseException: thrown by theUnitParser
In addition, I would create a new solution folder called Exceptions (no namespace generation), with all of the above classed inside it (personally, I think it kinda sucks to have all classes in one namespace, but I don't think we can easily move away from that..).
Finally, for the more typical exception-throwing scenarios, I'd like to add some static helper methods. Here's what I've come up so far:
internal static class ExceptionHelper
{
internal static ArgumentException CreateArgumentException<TUnit>(Enum unit, string argumentName)
where TUnit : struct, Enum
{
return new ArgumentException($"The given unit is of type {unit.GetType()}. The expected type is {typeof(TUnit)}.", argumentName);
}
internal static ArgumentException CreateArgumentException<TQuantity>(object obj, string argumentName)
where TQuantity : IQuantity
{
return new ArgumentException($"The given object is of type {obj.GetType()}. The expected type is {typeof(TQuantity)}.", argumentName);
}
internal static ArgumentException CreateArgumentOutOfRangeExceptionForNegativeTolerance(string argumentName)
{
return new ArgumentOutOfRangeException(argumentName, "The tolerance must be greater than or equal to 0");
}
internal static InvalidOperationException CreateInvalidOperationOnEmptyCollectionException()
{
return new InvalidOperationException("Sequence contains no elements");
}
internal static InvalidCastException CreateInvalidCastException<TQuantity, TOther>()
where TQuantity : IQuantity
{
return CreateInvalidCastException<TQuantity>(typeof(TOther));
}
internal static InvalidCastException CreateInvalidCastException<TQuantity>(Type targetType)
where TQuantity : IQuantity
{
return new InvalidCastException($"Converting {typeof(TQuantity)} to {targetType} is not supported.");
}
}We could probably move these strings to a resources dictionary, but I'm not too concerned with this ATM.
What I'm not sure about is whether the helpers should return or throw the exception.. If I'm not mistaken the second option, while a bit more convenient, would introduce an additional row in the stack trace compared to the first one. I know of the [DoesNotReturn] attribute but I don't think it affects the stack trace. What do you think?