From 40824a35fdc68debb8ef28275a7b75fb9cae32f9 Mon Sep 17 00:00:00 2001 From: Tristan Milnthorp Date: Wed, 3 Oct 2018 13:13:17 -0400 Subject: [PATCH] Reimplementing bool TryXXX methods in UnitConverter to now throw exceptions rather than wrapping in try/catch --- UnitsNet/UnitConverter.cs | 186 +++++++++++++++++++++++--------------- 1 file changed, 111 insertions(+), 75 deletions(-) diff --git a/UnitsNet/UnitConverter.cs b/UnitsNet/UnitConverter.cs index 654f44f37a..f4e25b8073 100644 --- a/UnitsNet/UnitConverter.cs +++ b/UnitsNet/UnitConverter.cs @@ -69,23 +69,38 @@ public static class UnitConverter /// /// double centimeters = ConvertByName(5, "Length", "Meter", "Centimeter"); // 500 /// Output value as the result of converting to . + /// No quantities were found that match . /// No units match the abbreviation. /// More than one unit matches the abbrevation. public static double ConvertByName(FromValue fromValue, string quantityName, string fromUnit, string toUnit) { - Type quantityType = GetQuantityType(quantityName); - Type unitType = GetUnitType(quantityName); + if(!TryGetQuantityType(quantityName, out var quantityType)) + throw new QuantityNotFoundException($"The given quantity name was not found: {quantityName}"); - object fromUnitValue = ParseUnit(unitType, fromUnit); // ex: LengthUnit.Meter - object toUnitValue = ParseUnit(unitType, toUnit); // ex: LengthUnit.Centimeter + if(!TryGetUnitType(quantityName, out var unitType)) + throw new UnitNotFoundException($"The unit type for the given quantity was not found: {quantityName}"); - MethodInfo fromMethod = GetStaticFromMethod(quantityType, unitType); // ex: UnitsNet.Length.From(double inputValue, LengthUnit inputUnit) - object fromResult = fromMethod.Invoke(null, new[] {fromValue, fromUnitValue}); // ex: Length quantity = UnitsNet.Length.From(5, LengthUnit.Meter) + if(!TryParseUnit(unitType, fromUnit, out var fromUnitValue)) // ex: LengthUnit.Meter + { + var e = new UnitNotFoundException($"Unit not found [{fromUnit}]."); + e.Data["unitName"] = fromUnit; + throw e; + } + + if(!TryParseUnit(unitType, toUnit, out var toUnitValue)) // ex: LengthUnit.Centimeter + { + var e = new UnitNotFoundException($"Unit not found [{toUnit}]."); + e.Data["unitName"] = toUnit; + throw e; + } + + var fromMethod = GetStaticFromMethod(quantityType, unitType); // ex: UnitsNet.Length.From(double inputValue, LengthUnit inputUnit) + var fromResult = fromMethod.Invoke(null, new[] {fromValue, fromUnitValue}); // ex: Length quantity = UnitsNet.Length.From(5, LengthUnit.Meter) - MethodInfo asMethod = GetAsMethod(quantityType, unitType); // ex: quantity.As(LengthUnit outputUnit) - object asResult = asMethod.Invoke(fromResult, new[] {toUnitValue}); // ex: double outputValue = quantity.As(LengthUnit.Centimeter) + var asMethod = GetAsMethod(quantityType, unitType); // ex: quantity.As(LengthUnit outputUnit) + var asResult = asMethod.Invoke(fromResult, new[] {toUnitValue}); // ex: double outputValue = quantity.As(LengthUnit.Centimeter) - return (double) asResult; + return (double)asResult; } /// @@ -117,21 +132,28 @@ public static double ConvertByName(FromValue fromValue, string quantityName, str /// True if conversion was successful. public static bool TryConvertByName(FromValue inputValue, string quantityName, string fromUnit, string toUnit, out double result) { - try - { - // Re-implement this to avoid exceptions where possible, as Try methods are generally recommended for performance and this is cheating. - // https://msdn.microsoft.com/en-us/library/ms229009(v=vs.100).aspx - // - // Implement Try-methods without try-catch #504 - // https://github.com/angularsen/UnitsNet/issues/504 - result = ConvertByName(inputValue, quantityName, fromUnit, toUnit); - return true; - } - catch - { - result = 0; + result = 0d; + + if(!TryGetQuantityType(quantityName, out var quantityType)) return false; - } + + if(!TryGetUnitType(quantityName, out var unitType)) + return false; + + if(!TryParseUnit(unitType, fromUnit, out var fromUnitValue)) // ex: LengthUnit.Meter + return false; + + if(!TryParseUnit(unitType, toUnit, out var toUnitValue)) // ex: LengthUnit.Centimeter + return false; + + var fromMethod = GetStaticFromMethod(quantityType, unitType); // ex: UnitsNet.Length.From(double inputValue, LengthUnit inputUnit) + var fromResult = fromMethod.Invoke(null, new[] {inputValue, fromUnitValue}); // ex: Length quantity = UnitsNet.Length.From(5, LengthUnit.Meter) + + var asMethod = GetAsMethod(quantityType, unitType); // ex: quantity.As(LengthUnit outputUnit) + var asResult = asMethod.Invoke(fromResult, new[] {toUnitValue}); // ex: double outputValue = quantity.As(LengthUnit.Centimeter) + + result = (double)asResult; + return true; } /// @@ -199,20 +221,23 @@ public static double ConvertByAbbreviation(FromValue fromValue, string quantityN /// More than one unit matches the abbrevation. public static double ConvertByAbbreviation(FromValue fromValue, string quantityName, string fromUnitAbbrev, string toUnitAbbrev, string culture) { - Type quantityType = GetQuantityType(quantityName); - Type unitType = GetUnitType(quantityName); + if(!TryGetQuantityType(quantityName, out var quantityType)) + throw new QuantityNotFoundException($"The given quantity name was not found: {quantityName}"); + + if(!TryGetUnitType(quantityName, out var unitType)) + throw new UnitNotFoundException($"The unit type for the given quantity was not found: {quantityName}"); - UnitSystem unitSystem = UnitSystem.GetCached(culture); - object fromUnitValue = unitSystem.Parse(fromUnitAbbrev, unitType); // ex: ("m", LengthUnit) => LengthUnit.Meter - object toUnitValue = unitSystem.Parse(toUnitAbbrev, unitType); // ex:("cm", LengthUnit) => LengthUnit.Centimeter + var unitSystem = UnitSystem.GetCached(culture); + var fromUnitValue = unitSystem.Parse(fromUnitAbbrev, unitType); // ex: ("m", LengthUnit) => LengthUnit.Meter + var toUnitValue = unitSystem.Parse(toUnitAbbrev, unitType); // ex:("cm", LengthUnit) => LengthUnit.Centimeter - MethodInfo fromMethod = GetStaticFromMethod(quantityType, unitType); // ex: UnitsNet.Length.From(double inputValue, LengthUnit inputUnit) - object fromResult = fromMethod.Invoke(null, new[] {fromValue, fromUnitValue}); // ex: Length quantity = UnitsNet.Length.From(5, LengthUnit.Meter) + var fromMethod = GetStaticFromMethod(quantityType, unitType); // ex: UnitsNet.Length.From(double inputValue, LengthUnit inputUnit) + var fromResult = fromMethod.Invoke(null, new[] {fromValue, fromUnitValue}); // ex: Length quantity = UnitsNet.Length.From(5, LengthUnit.Meter) - MethodInfo asMethod = GetAsMethod(quantityType, unitType); // ex: quantity.As(LengthUnit outputUnit) - object asResult = asMethod.Invoke(fromResult, new[] {toUnitValue}); // ex: double outputValue = quantity.As(LengthUnit.Centimeter) + var asMethod = GetAsMethod(quantityType, unitType); // ex: quantity.As(LengthUnit outputUnit) + var asResult = asMethod.Invoke(fromResult, new[] {toUnitValue}); // ex: double outputValue = quantity.As(LengthUnit.Centimeter) - return (double) asResult; + return (double)asResult; } /// @@ -278,23 +303,31 @@ public static bool TryConvertByAbbreviation(FromValue fromValue, string quantity public static bool TryConvertByAbbreviation(FromValue fromValue, string quantityName, string fromUnitAbbrev, string toUnitAbbrev, out double result, string culture) { - try - { - // Re-implement this to avoid exceptions where possible, as Try methods are generally recommended for performance and this is cheating. - // https://msdn.microsoft.com/en-us/library/ms229009(v=vs.100).aspx - // - // Implement Try-methods without try-catch #504 - // https://github.com/angularsen/UnitsNet/issues/504 - result = ConvertByAbbreviation(fromValue, quantityName, fromUnitAbbrev, toUnitAbbrev, culture); - return true; - } - catch - { - result = 0; + result = 0d; + + if(!TryGetQuantityType(quantityName, out var quantityType)) return false; - } - } + if(!TryGetUnitType(quantityName, out var unitType)) + return false; + + var unitSystem = UnitSystem.GetCached(culture); + + if(!unitSystem.TryParse(fromUnitAbbrev, unitType, out var fromUnitValue)) // ex: ("m", LengthUnit) => LengthUnit.Meter + return false; + + if(!unitSystem.TryParse(toUnitAbbrev, unitType, out var toUnitValue)) // ex:("cm", LengthUnit) => LengthUnit.Centimeter + return false; + + var fromMethod = GetStaticFromMethod(quantityType, unitType); // ex: UnitsNet.Length.From(double inputValue, LengthUnit inputUnit) + var fromResult = fromMethod.Invoke(null, new[] {fromValue, fromUnitValue}); // ex: Length quantity = UnitsNet.Length.From(5, LengthUnit.Meter) + + var asMethod = GetAsMethod(quantityType, unitType); // ex: quantity.As(LengthUnit outputUnit) + var asResult = asMethod.Invoke(fromResult, new[] {toUnitValue}); // ex: double outputValue = quantity.As(LengthUnit.Centimeter) + + result = (double)asResult; + return true; + } private static MethodInfo GetAsMethod(Type quantityType, Type unitType) { @@ -322,14 +355,14 @@ private static MethodInfo GetStaticFromMethod(Type quantityType, Type unitType) private static bool HasParameterTypes(MethodInfo methodInfo, params Type[] expectedTypes) { - ParameterInfo[] parameters = methodInfo.GetParameters(); + var parameters = methodInfo.GetParameters(); if (parameters.Length != expectedTypes.Length) throw new ArgumentException($"The number of parameters {parameters.Length} did not match the number of types {expectedTypes.Length}."); for (var i = 0; i < parameters.Length; i++) { - ParameterInfo p = parameters[i]; - Type t = expectedTypes[i]; + var p = parameters[i]; + var t = expectedTypes[i]; if (p.ParameterType != t) return false; } @@ -343,40 +376,43 @@ private static bool HasParameterTypes(MethodInfo methodInfo, params Type[] expec /// /// Unit type, such as . /// Unit name, such as "Meter" corresponding to . - /// Unit enum value, such as boxed as an object. + /// The return enum value, such as boxed as an object. + /// True if succeeded, otherwise false. /// No unit values match the . - private static object ParseUnit(Type unitType, string unitName) + private static bool TryParseUnit(Type unitType, string unitName, out object unitValue) { - object unitValue; // ex: LengthUnit.Meter - try - { - unitValue = Enum.Parse(unitType, unitName); - } - catch (Exception e) - { - var e2 = new UnitNotFoundException($"Unit not found [{unitName}].", e); - e2.Data["unitName"] = unitName; - throw e2; - } - return unitValue; + unitValue = null; + + if(!Enum.IsDefined(unitType, unitName)) + return false; + + unitValue = Enum.Parse(unitType, unitName); + if(unitValue == null) + return false; + + return true; } - private static Type GetUnitType(string quantityName) + private static bool TryGetUnitType(string quantityName, out Type unitType) { string unitTypeName = $"{UnitTypeNamespace}.{quantityName}Unit"; - Type unitType = UnitsNetAssembly.GetType(unitTypeName); // ex: UnitsNet.Units.LengthUnit enum - if (unitType == null) - throw new UnitNotFoundException($"Unit type name not found: {unitTypeName}"); - return unitType; + + unitType = UnitsNetAssembly.GetType(unitTypeName); // ex: UnitsNet.Units.LengthUnit enum + if(unitType == null) + return false; + + return true; } - private static Type GetQuantityType(string quantityName) + private static bool TryGetQuantityType(string quantityName, out Type quantityType) { string quantityTypeName = $"{QuantityNamespace}.{quantityName}"; - Type quantityType = UnitsNetAssembly.GetType(quantityTypeName); // ex: UnitsNet.Length struct - if (quantityType == null) - throw new QuantityNotFoundException($"Quantity type name not found: {quantityTypeName}"); - return quantityType; + + quantityType = UnitsNetAssembly.GetType(quantityTypeName); // ex: UnitsNet.Length struct + if(quantityType == null) + return false; + + return true; } } }