/// <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)); }
// 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)); }
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> /// 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 it is null and it is nullable, then return success with null if (input == null && destinationType.IsGenericType && destinationType.GetGenericTypeDefinition() == typeof(Nullable <>)) { return(Attempt <object> .Succeed(null)); } //if its not nullable and it is a value type if (input == null && destinationType.IsValueType) { return(Attempt <object> .Fail()); } //if the type can be null, then no problem if (input == null && destinationType.IsValueType == false) { return(Attempt <object> .Succeed(null)); } 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()); }
// 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... }