private static Nullable <Attempt <object> > TryConvertToFromString(this string input, Type destinationType) { if (destinationType == typeof(string)) { return(Attempt <object> .Succeed(input)); } if (string.IsNullOrEmpty(input)) { if (destinationType == typeof(Boolean)) { return(Attempt <object> .Succeed(false)); // special case for booleans, null/empty == false } if (destinationType == typeof(DateTime)) { return(Attempt <object> .Succeed(DateTime.MinValue)); } } // we have a non-empty string, look for type conversions in the expected order of frequency of use... if (destinationType.IsPrimitive) { if (destinationType == typeof(Int32)) { Int32 value; return(Int32.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } if (destinationType == typeof(Int64)) { Int64 value; return(Int64.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } if (destinationType == typeof(Boolean)) { Boolean value; if (Boolean.TryParse(input, out value)) { return(Attempt <object> .Succeed(value)); // don't declare failure so the CustomBooleanTypeConverter can try } } else if (destinationType == typeof(Int16)) { Int16 value; return(Int16.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } else if (destinationType == typeof(Double)) { Double value; return(Double.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } else if (destinationType == typeof(Single)) { Single value; return(Single.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } else if (destinationType == typeof(Char)) { Char value; return(Char.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } else if (destinationType == typeof(Byte)) { Byte value; return(Byte.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } else if (destinationType == typeof(SByte)) { SByte value; return(SByte.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } else if (destinationType == typeof(UInt32)) { UInt32 value; return(UInt32.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } else if (destinationType == typeof(UInt16)) { UInt16 value; return(UInt16.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } else if (destinationType == typeof(UInt64)) { UInt64 value; return(UInt64.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } } else if (destinationType == typeof(Guid)) { Guid value; return(Guid.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } else if (destinationType == typeof(DateTime)) { DateTime value; return(DateTime.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } else if (destinationType == typeof(DateTimeOffset)) { DateTimeOffset value; return(DateTimeOffset.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } else if (destinationType == typeof(TimeSpan)) { TimeSpan value; return(TimeSpan.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } else if (destinationType == typeof(Decimal)) { Decimal value; return(Decimal.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } else if (destinationType == typeof(Version)) { Version value; return(Version.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } // E_NOTIMPL IPAddress, BigInteger return(null); // we can't decide... }
/// <summary> /// Tries to convert the input object to the output type using TypeConverters. If the destination type is a superclass of the input type, /// if will use <see cref="Convert.ChangeType(object,System.Type)"/>. /// </summary> /// <param name="input">The input.</param> /// <param name="destinationType">Type of the destination.</param> /// <returns></returns> public static Attempt <object> TryConvertTo(this object input, Type destinationType) { if (input == null) { return(Attempt <object> .Fail()); } if (destinationType == typeof(object)) { return(Attempt.Succeed(input)); } if (input.GetType() == destinationType) { return(Attempt.Succeed(input)); } //check for string so that overloaders of ToString() can take advantage of the conversion. if (destinationType == typeof(string)) { return(Attempt <object> .Succeed(input.ToString())); } // if we've got a nullable of something, we try to convert directly to that thing. if (destinationType.IsGenericType && destinationType.GetGenericTypeDefinition() == typeof(Nullable <>)) { var underlyingType = Nullable.GetUnderlyingType(destinationType); //special case for empty strings for bools/dates which should return null if an empty string var asString = input as string; if (asString != null && string.IsNullOrEmpty(asString) && (underlyingType == typeof(DateTime) || underlyingType == typeof(bool))) { return(Attempt <object> .Succeed(null)); } // recursively call into myself with the inner (not-nullable) type and handle the outcome var nonNullable = input.TryConvertTo(underlyingType); // and if sucessful, fall on through to rewrap in a nullable; if failed, pass on the exception if (nonNullable.Success) { input = nonNullable.Result; // now fall on through... } else { return(Attempt <object> .Fail(nonNullable.Exception)); } } // we've already dealed with nullables, so any other generic types need to fall through if (!destinationType.IsGenericType) { if (input is string) { var result = TryConvertToFromString(input as string, destinationType); // if we processed the string (succeed or fail), we're done if (result.HasValue) { return(result.Value); } } //TODO: Do a check for destination type being IEnumerable<T> and source type implementing IEnumerable<T> with // the same 'T', then we'd have to find the extension method for the type AsEnumerable() and execute it. if (TypeHelper.IsTypeAssignableFrom(destinationType, input.GetType()) && TypeHelper.IsTypeAssignableFrom <IConvertible>(input)) { try { var casted = Convert.ChangeType(input, destinationType); return(Attempt.Succeed(casted)); } catch (Exception e) { return(Attempt <object> .Fail(e)); } } } var inputConverter = TypeDescriptor.GetConverter(input); if (inputConverter.CanConvertTo(destinationType)) { try { var converted = inputConverter.ConvertTo(input, destinationType); return(Attempt.Succeed(converted)); } catch (Exception e) { return(Attempt <object> .Fail(e)); } } if (destinationType == typeof(bool)) { var boolConverter = new CustomBooleanTypeConverter(); if (boolConverter.CanConvertFrom(input.GetType())) { try { var converted = boolConverter.ConvertFrom(input); return(Attempt.Succeed(converted)); } catch (Exception e) { return(Attempt <object> .Fail(e)); } } } var outputConverter = TypeDescriptor.GetConverter(destinationType); if (outputConverter.CanConvertFrom(input.GetType())) { try { var converted = outputConverter.ConvertFrom(input); return(Attempt.Succeed(converted)); } catch (Exception e) { return(Attempt <object> .Fail(e)); } } if (TypeHelper.IsTypeAssignableFrom <IConvertible>(input)) { try { var casted = Convert.ChangeType(input, destinationType); return(Attempt.Succeed(casted)); } catch (Exception e) { return(Attempt <object> .Fail(e)); } } return(Attempt <object> .Fail()); }
/// <summary> /// Attempts to convert the input object to the output type. /// </summary> /// <remarks>This code is an optimized version of the original Umbraco method</remarks> /// <param name="input">The input.</param> /// <param name="target">The type to convert to</param> /// <returns>The <see cref="Attempt{Object}"/></returns> public static Attempt <object> TryConvertTo(this object input, Type target) { if (target == null) { return(Attempt <object> .Fail()); } try { if (input == null) { // Nullable is ok if (target.IsGenericType && GetCachedGenericNullableType(target) != null) { return(Attempt <object> .Succeed(null)); } // Reference types are ok return(Attempt <object> .SucceedIf(target.IsValueType == false, null)); } var inputType = input.GetType(); // Easy if (target == typeof(object) || inputType == target) { return(Attempt.Succeed(input)); } // Check for string so that overloaders of ToString() can take advantage of the conversion. if (target == typeof(string)) { return(Attempt <object> .Succeed(input.ToString())); } // If we've got a nullable of something, we try to convert directly to that thing. // We cache the destination type and underlying nullable types // Any other generic types need to fall through if (target.IsGenericType) { var underlying = GetCachedGenericNullableType(target); if (underlying != null) { // Special case for empty strings for bools/dates which should return null if an empty string. if (input is string inputString) { //TODO: Why the check against only bool/date when a string is null/empty? In what scenario can we convert to another type when the string is null or empty other than just being null? if (string.IsNullOrEmpty(inputString) && (underlying == typeof(DateTime) || underlying == typeof(bool))) { return(Attempt <object> .Succeed(null)); } } // Recursively call into this method with the inner (not-nullable) type and handle the outcome var inner = input.TryConvertTo(underlying); // And if sucessful, fall on through to rewrap in a nullable; if failed, pass on the exception if (inner.Success) { input = inner.Result; // Now fall on through... } else { return(Attempt <object> .Fail(inner.Exception)); } } } else { // target is not a generic type if (input is string inputString) { // Try convert from string, returns an Attempt if the string could be // processed (either succeeded or failed), else null if we need to try // other methods var result = TryConvertToFromString(inputString, target); if (result.HasValue) { return(result.Value); } } // TODO: Do a check for destination type being IEnumerable<T> and source type implementing IEnumerable<T> with // the same 'T', then we'd have to find the extension method for the type AsEnumerable() and execute it. if (GetCachedCanAssign(input, inputType, target)) { return(Attempt.Succeed(Convert.ChangeType(input, target))); } } if (target == typeof(bool)) { if (GetCachedCanConvertToBoolean(inputType)) { return(Attempt.Succeed(CustomBooleanTypeConverter.ConvertFrom(input))); } } var inputConverter = GetCachedSourceTypeConverter(inputType, target); if (inputConverter != null) { return(Attempt.Succeed(inputConverter.ConvertTo(input, target))); } var outputConverter = GetCachedTargetTypeConverter(inputType, target); if (outputConverter != null) { return(Attempt.Succeed(outputConverter.ConvertFrom(input))); } if (target.IsGenericType && GetCachedGenericNullableType(target) != null) { // cannot Convert.ChangeType as that does not work with nullable // input has already been converted to the underlying type - just // return input, there's an implicit conversion from T to T? anyways return(Attempt.Succeed(input)); } // Re-check convertables since we altered the input through recursion if (input is IConvertible convertible2) { return(Attempt.Succeed(Convert.ChangeType(convertible2, target))); } } catch (Exception e) { return(Attempt <object> .Fail(e)); } return(Attempt <object> .Fail()); }
/// <summary> /// Attempts to convert the input string to the output type. /// </summary> /// <remarks>This code is an optimized version of the original Umbraco method</remarks> /// <param name="input">The input.</param> /// <param name="target">The type to convert to</param> /// <returns>The <see cref="Nullable{Attempt}"/></returns> private static Attempt <object>?TryConvertToFromString(this string input, Type target) { // Easy if (target == typeof(string)) { return(Attempt <object> .Succeed(input)); } // Null, empty, whitespaces if (string.IsNullOrWhiteSpace(input)) { if (target == typeof(bool)) { // null/empty = bool false return(Attempt <object> .Succeed(false)); } if (target == typeof(DateTime)) { // null/empty = min DateTime value return(Attempt <object> .Succeed(DateTime.MinValue)); } // Cannot decide here, // Any of the types below will fail parsing and will return a failed attempt // but anything else will not be processed and will return null // so even though the string is null/empty we have to proceed. } // Look for type conversions in the expected order of frequency of use. // // By using a mixture of ordered if statements and switches we can optimize both for // fast conditional checking for most frequently used types and the branching // that does not depend on previous values available to switch statements. if (target.IsPrimitive) { if (target == typeof(int)) { if (int.TryParse(input, out var value)) { return(Attempt <object> .Succeed(value)); } // Because decimal 100.01m will happily convert to integer 100, it // makes sense that string "100.01" *also* converts to integer 100. var input2 = NormalizeNumberDecimalSeparator(input); return(Attempt <object> .SucceedIf(decimal.TryParse(input2, out var value2), Convert.ToInt32(value2))); } if (target == typeof(long)) { if (long.TryParse(input, out var value)) { return(Attempt <object> .Succeed(value)); } // Same as int var input2 = NormalizeNumberDecimalSeparator(input); return(Attempt <object> .SucceedIf(decimal.TryParse(input2, out var value2), Convert.ToInt64(value2))); } // TODO: Should we do the decimal trick for short, byte, unsigned? if (target == typeof(bool)) { if (bool.TryParse(input, out var value)) { return(Attempt <object> .Succeed(value)); } // Don't declare failure so the CustomBooleanTypeConverter can try return(null); } // Calling this method directly is faster than any attempt to cache it. switch (Type.GetTypeCode(target)) { case TypeCode.Int16: return(Attempt <object> .SucceedIf(short.TryParse(input, out var value), value)); case TypeCode.Double: var input2 = NormalizeNumberDecimalSeparator(input); return(Attempt <object> .SucceedIf(double.TryParse(input2, out var valueD), valueD)); case TypeCode.Single: var input3 = NormalizeNumberDecimalSeparator(input); return(Attempt <object> .SucceedIf(float.TryParse(input3, out var valueF), valueF)); case TypeCode.Char: return(Attempt <object> .SucceedIf(char.TryParse(input, out var valueC), valueC)); case TypeCode.Byte: return(Attempt <object> .SucceedIf(byte.TryParse(input, out var valueB), valueB)); case TypeCode.SByte: return(Attempt <object> .SucceedIf(sbyte.TryParse(input, out var valueSb), valueSb)); case TypeCode.UInt32: return(Attempt <object> .SucceedIf(uint.TryParse(input, out var valueU), valueU)); case TypeCode.UInt16: return(Attempt <object> .SucceedIf(ushort.TryParse(input, out var valueUs), valueUs)); case TypeCode.UInt64: return(Attempt <object> .SucceedIf(ulong.TryParse(input, out var valueUl), valueUl)); } } else if (target == typeof(Guid)) { return(Attempt <object> .SucceedIf(Guid.TryParse(input, out var value), value)); } else if (target == typeof(DateTime)) { if (DateTime.TryParse(input, out var value)) { switch (value.Kind) { case DateTimeKind.Unspecified: case DateTimeKind.Utc: return(Attempt <object> .Succeed(value)); case DateTimeKind.Local: return(Attempt <object> .Succeed(value.ToUniversalTime())); default: throw new ArgumentOutOfRangeException(); } } return(Attempt <object> .Fail()); } else if (target == typeof(DateTimeOffset)) { return(Attempt <object> .SucceedIf(DateTimeOffset.TryParse(input, out var value), value)); } else if (target == typeof(TimeSpan)) { return(Attempt <object> .SucceedIf(TimeSpan.TryParse(input, out var value), value)); } else if (target == typeof(decimal)) { var input2 = NormalizeNumberDecimalSeparator(input); return(Attempt <object> .SucceedIf(decimal.TryParse(input2, out var value), value)); } else if (input != null && target == typeof(Version)) { return(Attempt <object> .SucceedIf(Version.TryParse(input, out var value), value)); } // E_NOTIMPL IPAddress, BigInteger return(null); // We can't decide... }
private static Nullable <Attempt <object> > TryConvertToFromString(this string input, Type destinationType) { if (destinationType == typeof(string)) { return(Attempt <object> .Succeed(input)); } if (string.IsNullOrEmpty(input)) { if (destinationType == typeof(Boolean)) { return(Attempt <object> .Succeed(false)); // special case for booleans, null/empty == false } if (destinationType == typeof(DateTime)) { return(Attempt <object> .Succeed(DateTime.MinValue)); } } // we have a non-empty string, look for type conversions in the expected order of frequency of use... if (destinationType.IsPrimitive) { if (destinationType == typeof(Int32)) { Int32 value; return(Int32.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } if (destinationType == typeof(Int64)) { Int64 value; return(Int64.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } if (destinationType == typeof(Boolean)) { Boolean value; if (Boolean.TryParse(input, out value)) { return(Attempt <object> .Succeed(value)); // don't declare failure so the CustomBooleanTypeConverter can try } } else if (destinationType == typeof(Int16)) { Int16 value; return(Int16.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } else if (destinationType == typeof(Double)) { Double value; var input2 = NormalizeNumberDecimalSeparator(input); return(Double.TryParse(input2, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } else if (destinationType == typeof(Single)) { Single value; var input2 = NormalizeNumberDecimalSeparator(input); return(Single.TryParse(input2, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } else if (destinationType == typeof(Char)) { Char value; return(Char.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } else if (destinationType == typeof(Byte)) { Byte value; return(Byte.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } else if (destinationType == typeof(SByte)) { SByte value; return(SByte.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } else if (destinationType == typeof(UInt32)) { UInt32 value; return(UInt32.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } else if (destinationType == typeof(UInt16)) { UInt16 value; return(UInt16.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } else if (destinationType == typeof(UInt64)) { UInt64 value; return(UInt64.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } } else if (destinationType == typeof(Guid)) { Guid value; return(Guid.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } else if (destinationType == typeof(DateTime)) { DateTime value; if (DateTime.TryParse(input, out value)) { switch (value.Kind) { case DateTimeKind.Unspecified: case DateTimeKind.Utc: return(Attempt <object> .Succeed(value)); case DateTimeKind.Local: return(Attempt <object> .Succeed(value.ToUniversalTime())); default: throw new ArgumentOutOfRangeException(); } } return(Attempt <object> .Fail()); } else if (destinationType == typeof(DateTimeOffset)) { DateTimeOffset value; return(DateTimeOffset.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } else if (destinationType == typeof(TimeSpan)) { TimeSpan value; return(TimeSpan.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } else if (destinationType == typeof(Decimal)) { Decimal value; var input2 = NormalizeNumberDecimalSeparator(input); return(Decimal.TryParse(input2, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } else if (destinationType == typeof(Version)) { Version value; return(Version.TryParse(input, out value) ? Attempt <object> .Succeed(value) : Attempt <object> .Fail()); } // E_NOTIMPL IPAddress, BigInteger return(null); // we can't decide... }
/// <summary> /// Creates a failed attempt with a result, an exception and a status. /// </summary> /// <typeparam name="TResult">The type of the attempted operation result.</typeparam> /// <typeparam name="TStatus">The type of the attempted operation status.</typeparam> /// <param name="status">The status of the attempt.</param> /// <param name="result">The result of the attempt.</param> /// <param name="exception">The exception causing the failure of the attempt.</param> /// <returns>The failed attempt.</returns> public static Attempt <TResult, TStatus> FailWithStatus <TResult, TStatus>(TStatus status, TResult result, Exception exception) { return(Attempt <TResult, TStatus> .Fail(status, result, exception)); }
/// <summary> /// Creates a failed attempt with a result. /// </summary> /// <typeparam name="TResult">The type of the attempted operation result.</typeparam> /// <param name="result">The result of the attempt.</param> /// <returns>The failed attempt.</returns> public static Attempt <TResult> Fail <TResult>(TResult result) { return(Attempt <TResult> .Fail(result)); }
/// <summary> /// Creates a failed attempt with a result and an exception. /// </summary> /// <typeparam name="T">The type of the attempted operation result.</typeparam> /// <param name="result">The result of the attempt.</param> /// <param name="exception">The exception causing the failure of the attempt.</param> /// <returns>The failed attempt.</returns> public static Attempt <T> Fail <T>(T result, Exception exception) { return(Attempt <T> .Fail(result, exception)); }
/// <summary> /// Creates a successful attempt with a result and a status. /// </summary> /// <typeparam name="TResult">The type of the attempted operation result.</typeparam> /// <typeparam name="TStatus">The type of the attempted operation status.</typeparam> /// <param name="status">The status of the attempt.</param> /// <param name="result">The result of the attempt.</param> /// <returns>The successful attempt.</returns> public static Attempt <TResult, TStatus> SucceedWithStatus <TResult, TStatus>(TStatus status, TResult result) { return(Attempt <TResult, TStatus> .Succeed(status, result)); }
/// <summary> /// Creates a failed attempt. /// </summary> /// <typeparam name="TResult">The type of the attempted operation result.</typeparam> /// <returns>The failed attempt.</returns> public static Attempt <TResult> Fail <TResult>() { return(Attempt <TResult> .Fail()); }
// note: // cannot rely on overloads only to differenciate between with/without status // in some cases it will always be ambiguous, so be explicit w/ 'WithStatus' methods /// <summary> /// Creates a successful attempt with a result. /// </summary> /// <typeparam name="TResult">The type of the attempted operation result.</typeparam> /// <param name="result">The result of the attempt.</param> /// <returns>The successful attempt.</returns> public static Attempt <TResult> Succeed <TResult>(TResult result) { return(Attempt <TResult> .Succeed(result)); }
/// <summary> /// Creates a successful or a failed attempt, with a result. /// </summary> /// <typeparam name="TResult">The type of the attempted operation result.</typeparam> /// <typeparam name="TStatus">The type of the attempted operation status.</typeparam> /// <param name="condition">A value indicating whether the attempt is successful.</param> /// <param name="succStatus">The status of the successful attempt.</param> /// <param name="failStatus">The status of the failed attempt.</param> /// <param name="result">The result of the attempt.</param> /// <returns>The attempt.</returns> public static Attempt <TResult, TStatus> IfWithStatus <TResult, TStatus>(bool condition, TStatus succStatus, TStatus failStatus, TResult result) { return(Attempt <TResult, TStatus> .If(condition, succStatus, failStatus, result)); }
/// <summary> /// Creates a successful or a failed attempt, with a result. /// </summary> /// <typeparam name="TResult">The type of the attempted operation result.</typeparam> /// <param name="condition">A value indicating whether the attempt is successful.</param> /// <param name="result">The result of the attempt.</param> /// <returns>The attempt.</returns> public static Attempt <TResult> If <TResult>(bool condition, TResult result) { return(Attempt <TResult> .If(condition, result)); }
/// <summary> /// Creates a successful attempt with a result. /// </summary> /// <typeparam name="T">The type of the attempted operation result.</typeparam> /// <param name="result">The result of the attempt.</param> /// <returns>The successful attempt.</returns> public static Attempt <T> Succeed <T>(T result) { return(Attempt <T> .Succeed(result)); }
/// <summary> /// Creates a failed attempt with a result and a status. /// </summary> /// <typeparam name="TResult">The type of the attempted operation result.</typeparam> /// <typeparam name="TStatus">The type of the attempted operation status.</typeparam> /// <param name="status">The status of the attempt.</param> /// <param name="result">The result of the attempt.</param> /// <returns>The failed attempt.</returns> public static Attempt <TResult, TStatus> FailWithStatus <TResult, TStatus>(TStatus status, TResult result) { return(Attempt <TResult, TStatus> .Fail(status, result)); }
/// <summary> /// Creates a failed attempt with a result. /// </summary> /// <typeparam name="T">The type of the attempted operation result.</typeparam> /// <param name="result">The result of the attempt.</param> /// <returns>The failed attempt.</returns> public static Attempt <T> Fail <T>(T result) { return(Attempt <T> .Fail(result)); }
/// <summary> /// Creates a failed attempt with a result and an exception. /// </summary> /// <typeparam name="TResult">The type of the attempted operation result.</typeparam> /// <param name="result">The result of the attempt.</param> /// <param name="exception">The exception causing the failure of the attempt.</param> /// <returns>The failed attempt.</returns> public static Attempt <TResult> Fail <TResult>(TResult result, Exception exception) { return(Attempt <TResult> .Fail(result, exception)); }
/// <summary> /// Creates a successful or a failed attempt, with a result. /// </summary> /// <typeparam name="T">The type of the attempted operation result.</typeparam> /// <param name="success">A value indicating whether the attempt is successful.</param> /// <param name="result">The result of the attempt.</param> /// <returns>The attempt.</returns> public static Attempt <T> If <T>(bool success, T result) { return(Attempt <T> .SucceedIf(success, result)); }
// returns an attempt if the string has been processed (either succeeded or failed) // returns null if we need to try other methods private static Attempt <object>?TryConvertToFromString(this string input, Type destinationType) { // easy if (destinationType == typeof(string)) { return(Attempt <object> .Succeed(input)); } // null, empty, whitespaces if (string.IsNullOrWhiteSpace(input)) { if (destinationType == typeof(bool)) // null/empty = bool false { return(Attempt <object> .Succeed(false)); } if (destinationType == typeof(DateTime)) // null/empty = min DateTime value { return(Attempt <object> .Succeed(DateTime.MinValue)); } // cannot decide here, // any of the types below will fail parsing and will return a failed attempt // but anything else will not be processed and will return null // so even though the string is null/empty we have to proceed } // look for type conversions in the expected order of frequency of use... if (destinationType.IsPrimitive) { if (destinationType == typeof(int)) // aka Int32 { int value; if (int.TryParse(input, out value)) { return(Attempt <object> .Succeed(value)); } // because decimal 100.01m will happily convert to integer 100, it // makes sense that string "100.01" *also* converts to integer 100. decimal value2; var input2 = NormalizeNumberDecimalSeparator(input); return(Attempt <object> .SucceedIf(decimal.TryParse(input2, out value2), Convert.ToInt32(value2))); } if (destinationType == typeof(long)) // aka Int64 { long value; if (long.TryParse(input, out value)) { return(Attempt <object> .Succeed(value)); } // same as int decimal value2; var input2 = NormalizeNumberDecimalSeparator(input); return(Attempt <object> .SucceedIf(decimal.TryParse(input2, out value2), Convert.ToInt64(value2))); } // fixme - should we do the decimal trick for short, byte, unsigned? if (destinationType == typeof(bool)) // aka Boolean { bool value; if (bool.TryParse(input, out value)) { return(Attempt <object> .Succeed(value)); } // don't declare failure so the CustomBooleanTypeConverter can try return(null); } if (destinationType == typeof(short)) // aka Int16 { short value; return(Attempt <object> .SucceedIf(short.TryParse(input, out value), value)); } if (destinationType == typeof(double)) // aka Double { double value; var input2 = NormalizeNumberDecimalSeparator(input); return(Attempt <object> .SucceedIf(double.TryParse(input2, out value), value)); } if (destinationType == typeof(float)) // aka Single { float value; var input2 = NormalizeNumberDecimalSeparator(input); return(Attempt <object> .SucceedIf(float.TryParse(input2, out value), value)); } if (destinationType == typeof(char)) // aka Char { char value; return(Attempt <object> .SucceedIf(char.TryParse(input, out value), value)); } if (destinationType == typeof(byte)) // aka Byte { byte value; return(Attempt <object> .SucceedIf(byte.TryParse(input, out value), value)); } if (destinationType == typeof(sbyte)) // aka SByte { sbyte value; return(Attempt <object> .SucceedIf(sbyte.TryParse(input, out value), value)); } if (destinationType == typeof(uint)) // aka UInt32 { uint value; return(Attempt <object> .SucceedIf(uint.TryParse(input, out value), value)); } if (destinationType == typeof(ushort)) // aka UInt16 { ushort value; return(Attempt <object> .SucceedIf(ushort.TryParse(input, out value), value)); } if (destinationType == typeof(ulong)) // aka UInt64 { ulong value; return(Attempt <object> .SucceedIf(ulong.TryParse(input, out value), value)); } } else if (destinationType == typeof(Guid)) { Guid value; return(Attempt <object> .SucceedIf(Guid.TryParse(input, out value), value)); } else if (destinationType == typeof(DateTime)) { DateTime value; if (DateTime.TryParse(input, out value)) { switch (value.Kind) { case DateTimeKind.Unspecified: case DateTimeKind.Utc: return(Attempt <object> .Succeed(value)); case DateTimeKind.Local: return(Attempt <object> .Succeed(value.ToUniversalTime())); default: throw new ArgumentOutOfRangeException(); } } return(Attempt <object> .Fail()); } else if (destinationType == typeof(DateTimeOffset)) { DateTimeOffset value; return(Attempt <object> .SucceedIf(DateTimeOffset.TryParse(input, out value), value)); } else if (destinationType == typeof(TimeSpan)) { TimeSpan value; return(Attempt <object> .SucceedIf(TimeSpan.TryParse(input, out value), value)); } else if (destinationType == typeof(decimal)) // aka Decimal { decimal value; var input2 = NormalizeNumberDecimalSeparator(input); return(Attempt <object> .SucceedIf(decimal.TryParse(input2, out value), value)); } else if (destinationType == typeof(Version)) { Version value; return(Attempt <object> .SucceedIf(Version.TryParse(input, out value), value)); } // E_NOTIMPL IPAddress, BigInteger return(null); // we can't decide... }