public static Range <decimal> AsNumericRange(object range) { if (range == null || !IsNumericRange(range)) { return(null); } dynamic r = range; // Using decimal for precision var numeric = new Range <decimal>() { IsMaxInclusive = r.IsMaxInclusive, IsMinInclusive = r.IsMinInclusive, MinValue = r.MinValue == null ? (decimal?)null : SmartConverter.Convert <decimal>( r.MinValue.ToString(), r.IsMinInclusive ? ConvertingKind.MinInclusive : ConvertingKind.MinExclusive ), MaxValue = r.MaxValue == null ? (decimal?)null : SmartConverter.Convert <decimal>( r.MaxValue.ToString(), r.IsMaxInclusive ? ConvertingKind.MaxInclusive : ConvertingKind.MaxExclusive ) }; return(numeric); }
/// <summary> /// Try to parse an IRange<T> from a string. /// Returns RangeResult<T> to avoid throwing an exception. public static RangeResult <T> GetResultFromString <T>(string value) where T : struct { var range = new Range <T>(); /** * This expression is very cumbersome due to its requirement of handling * several formats. Please take note of the lookbehind assertion and its preceeding * word-boundary. The former lets us define a max-value using simple-syntax while * the latter accounts for the min-value (due to the assertion). * Valid formats (not exhaustive): * [123,456] * [123, * [123,456 * 123 * 123,456 * 123, */ var parsedValue = Regex.Match( value + ",", @"(?<startSyntax>[\[\(])?(?<minValue>[^,[(]+?)?(?:[\W])(?<=,)(?<maxValue>[^,\]\)]+)?(?<endSyntax>[\]\)])?"); if (!parsedValue.Success) { return(RangeResult <T> .Error("value does not match expected format.")); } else { var groups = parsedValue.Groups; var parsedMinValue = groups["minValue"].Value; /** * If we using short-range syntax, then we * want to set the max-value if not provided. */ var parsedMaxValue = string.IsNullOrEmpty( groups["maxValue"].Value + groups["startSyntax"].Value + groups["endSyntax"].Value) ? parsedMinValue : groups["maxValue"].Value; var isMinInclusive = (groups["startSyntax"].Value == "[" || string.IsNullOrEmpty(groups["startSyntax"].Value)) ? true : false; var isMaxInclusive = (groups["endSyntax"].Value == "]" || string.IsNullOrEmpty(groups["endSyntax"].Value)) ? true : false; var isMinInfinite = parsedMinValue == "-∞" ? true : false; var isMaxInfinite = parsedMaxValue == "+∞" ? true : false; if (string.IsNullOrWhiteSpace(parsedMinValue) && string.IsNullOrWhiteSpace(parsedMaxValue)) { return(RangeResult <T> .Error("value cannot be open-ended for both min and max-values.")); } if (isMinInclusive && isMinInfinite) { return(RangeResult <T> .Error("value cannot have inclusive infinite lower-bound.")); } if (isMaxInclusive && isMaxInfinite) { return(RangeResult <T> .Error("value cannot have inclusive infinite upper-bound.")); } T?minValue = default(T?); T?maxValue = default(T?); if (!string.IsNullOrWhiteSpace(parsedMinValue) && !isMinInfinite) { try { minValue = SmartConverter.Convert <T>( parsedMinValue, isMinInclusive ? ConvertingKind.MinInclusive : ConvertingKind.MinExclusive ); } catch (Exception) { return(RangeResult <T> .Error( string.Format("parsed minimum value `{0}` does not match expected type of `{1}`.", groups["minValue"].Value, typeof(T).Name))); } } if (!string.IsNullOrWhiteSpace(parsedMaxValue) && !isMaxInfinite) { try { maxValue = SmartConverter.Convert <T>(parsedMaxValue, isMaxInclusive ? ConvertingKind.MaxInclusive : ConvertingKind.MaxExclusive ); } catch (Exception) { return(RangeResult <T> .Error( string.Format("parsed maximum value `{0}` does not match expected type of `{1}`.", groups["maxValue"].Value, typeof(T).Name))); } } range.MinValue = minValue; range.MaxValue = maxValue; range.IsMinInclusive = isMinInclusive; range.IsMaxInclusive = isMaxInclusive; } if (range.MinValue.HasValue && range.MaxValue.HasValue) { var anyInclusiveRanges = range.IsMinInclusive || range.IsMaxInclusive; var compareResult = Comparer <T> .Default.Compare(range.MinValue.Value, range.MaxValue.Value); if (anyInclusiveRanges && compareResult > 0) { return(RangeResult <T> .Error( "Minimum value of range must be less than or equal to maximum value.")); } if (!anyInclusiveRanges && compareResult >= 0) { return(RangeResult <T> .Error( "Minimum value of range must be less than maximum value when range is non-inclusive.")); } } return(RangeResult <T> .Success(range)); }