예제 #1
0
 /// <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));
 }
예제 #2
0
        /// <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 null...
            if (input == null)
            {
                // nullable is ok
                if (destinationType.IsGenericType && destinationType.GetGenericTypeDefinition() == typeof(Nullable <>))
                {
                    return(Attempt <object> .Succeed(null));
                }

                // value type is nok, else can be null, so is ok
                return(Attempt <object> .SucceedIf(destinationType.IsValueType == false, null));
            }

            // easy
            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 == false)
            {
                if (input is string)
                {
                    // 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(input as string, destinationType);
                    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());
        }
예제 #3
0
        /// <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());
        }
예제 #4
0
        // 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...
        }