예제 #1
0
        /// <summary>
        /// Test if the input value is a non-null numeric type
        /// or a string that can be parsed as a number, with detection
        /// of hex strings controlled by <paramref name="parseFlags"/>
        /// </summary>
        /// <param name="value"></param>
        /// <param name="parseFlags"></param>
        public static bool IsNumeric(object value, ParseNumericStringFlags parseFlags)
        {
            if (null == value)
            {
                return(false);
            }
            if (value is string sValue)
            {
                if (double.TryParse(sValue, out _))
                {
                    return(true);
                }

                bool allowDigitSep = (parseFlags & ParseNumericStringFlags.AllowDigitSeparator) != 0;
                if (allowDigitSep)
                {
                    bool changed = false;
                    while (sValue.IndexOf('_') >= 0)
                    {
                        sValue  = Regex.Replace(sValue, "([0-9a-fA-F])_+([0-9a-fA-F])", "$1$2");
                        changed = true;
                    }

                    if (changed && double.TryParse(sValue, out _))
                    {
                        return(true);
                    }
                }

                if ((parseFlags & ParseNumericStringFlags.HexString) != 0)
                {
                    if (Regex.IsMatch(sValue, @"^\s*0[xX][0-9a-fA-F]+$"))
                    {
                        return(true);
                    }
                }

                if ((parseFlags & ParseNumericStringFlags.OctalString) != 0)
                {
                    if (Regex.IsMatch(sValue, @"^\s*0[oO][0-7]+$"))
                    {
                        return(true);
                    }
                }

                if ((parseFlags & ParseNumericStringFlags.BinaryString) != 0)
                {
                    if (Regex.IsMatch(sValue, @"^\s*0[bB][01]+$"))
                    {
                        return(true);
                    }
                }

                return(false);
            }
            return(TypeInspection.IsNumberType(value.GetType()));
        }
        private static TimeSpan _ToTimeSpan(object value)
        {
            if (value is TimeSpan)
            {
                return((TimeSpan)value);
            }
            if (value is string && Regex.IsMatch((string)value, @"^\s*\d+\.\d+\s*$"))
            {
                // Treat decimal string as seconds, not ticks
                return(TimeSpan.FromSeconds(To <double>(value)));
            }
            else if ((value != null && TypeInspection.IsNumberType(value.GetType())) || ValueInspection.IsNumeric(value))
            {
                return(TimeSpan.FromTicks(To <long>(value)));
            }
            else if (value is string)
            {
                string sValue = (string)value;
                var    m      = Regex.Match(sValue, RX_MINUTES);
                if (m.Success)
                {
                    // The input is a minutes : seconds string. The built-in TimeSpan.Parse requires a leading
                    // hours segment as well. Append the 0: hours segment, and also pad the minutes segment with
                    // a leading 0 if necessary
                    sValue = "0:" + (m.Groups["minutes"].Value.Length == 2 ? "" : "0") + sValue;
                }

                return(TimeSpan.Parse(sValue));
            }
            else if (value is DateTime)
            {
                return(TimeSpan.FromTicks(((DateTime)value).Ticks));
            }

            // If the input is not a TimeSpan, this will throw an invalid cast exception in the localized language:
            return((TimeSpan)value);
        }
 /// <summary>
 /// Test if the input value is a non-null numeric type
 /// or a string that can be parsed as a number, with detection
 /// of hex strings controlled by ConvertOptions flags
 /// </summary>
 /// <param name="value"></param>
 /// <param name="options"></param>
 public static bool IsNumeric(object value, ConvertOptions options)
 {
     if (value == null)
     {
         return(false);
     }
     if (value is string sValue)
     {
         if (double.TryParse(sValue, out double d))
         {
             return(true);
         }
         if (options.HasFlag(ConvertOptions.AllowVBHex) && Regex.IsMatch(sValue, @"^\s*&[hH][0-9a-fA-F]+$"))
         {
             return(true);
         }
         if (options.HasFlag(ConvertOptions.Allow0xHex) && Regex.IsMatch(sValue, @"^\s*0[xX][0-9a-fA-F]+$"))
         {
             return(true);
         }
         return(false);
     }
     return(TypeInspection.IsNumberType(value.GetType()));
 }
        internal static object To(object value, Type targetType, ConvertOptions options, bool ignoreError, object defaultValue)
        {
            var typeInfo = targetType.GetTypeInfo();

            // Quick check #1: If input is null reference and target type is a reference type we can always safely return nothing
            if ((value == null) && (!typeInfo.IsValueType))
            {
                return(null);
            }

            // Quick check #2: If the input already is of the target type the return it immediately
            if (targetType.IsInstanceOfType(value))
            {
                return(value);
            }

            if (typeInfo.IsValueType)
            {
                // ---------------------------------------------------------------------------
                //  Special treatment of Nullable<T>. Although both C# and VB allow assignment
                //  and comparison of null literal to Nullable<T> values, the underlying type
                //  is actually a non-nullable struct (value type), thus we handle nullable
                //  types inside the section that handles value types
                // ---------------------------------------------------------------------------
                if (TypeInspection.IsNullableOfT(targetType))
                {
                    if (ValueInspection.IsNull(value, options))
                    {
                        // Nullable<T> of T is technically a struct, so IsValueType is true. However,
                        // semantics and implementation allow it to be treated as a reference type,
                        // so return null if the input is empty
                        return(null);
                    }
                    else
                    {
                        // Input is not an empty value, so convert to the underying type
                        try
                        {
                            // Note: DON'T ignore error on underlying conversion type
                            return(To(value, Nullable.GetUnderlyingType(targetType), options, false, null));
                        }
                        catch
                        {
                            if (ignoreError)
                            {
                                return(defaultValue ?? null);
                            }
                            throw;
                        }
                    }
                } // end IsNullable<T>

                // If input is empty, we can immediately return default or throw an exception
                if (ValueInspection.IsNull(value, options))
                {
                    if (options.HasFlag(ConvertOptions.NullToValueDefault))
                    {
                        //This is how we return the default value of a value type:
                        return(defaultValue ?? Activator.CreateInstance(targetType));
                    }
                    else
                    {
                        if (ignoreError)
                        {
                            return(defaultValue ?? Activator.CreateInstance(targetType));
                        }
                        throw new ArgumentNullException("value", "Cannot convert empty input value to value type " + targetType.FullName);
                    }
                }

                // We only got this far if the target type is a value type and the input value is not empty
                try
                {
                    // ---------------------------------------------------------------------------
                    //  Special treatment of Enums
                    // ---------------------------------------------------------------------------
                    if (typeInfo.IsEnum)
                    {
                        // Test IsNumeric BEFORE testing string so that numeric strings are
                        // treated as numbers, not as enum member names
                        if (ValueInspection.IsNumeric(value, options))
                        {
                            // If the input is a number or *numeric string*, first convert the
                            // input to an enum number value, then cast it using Enum.ToObject

                            // Note: DON'T ignore error on underlying conversion type
                            var rawValue = To(value, Enum.GetUnderlyingType(targetType), options, false, null);
                            return(Enum.ToObject(targetType, rawValue));
                        }
                        else if (value is string)
                        {
                            // Input is a string but Information.IsNumeric did not recognize it
                            // as a number. So, treat the input as an enum member name
                            string sEnumName = Regex.Replace((string)value, @"[\s\r\n]+", "");
                            return(Enum.Parse(targetType, sEnumName, true));
                        }
                        else
                        {
                            // Fallback: Attempt to convert the input to the underlying numeric type, even if
                            // Information.IsNumeric returned false

                            // Note: DON'T ignore error on underlying conversion type
                            var rawValue = To(value, Enum.GetUnderlyingType(targetType), options, false, null);
                            return(Enum.ToObject(targetType, rawValue));
                        }
                    } // end IsEnum

                    // ---------------------------------------------------------------------------
                    //  Use hand-written conversion functions for specific types
                    // ---------------------------------------------------------------------------
                    if (targetType == typeof(Guid))
                    {
                        // Use special conversion logic for converting to a Guid
                        return(_ToGuid(value));
                    }
                    else if (targetType == typeof(TimeSpan))
                    {
                        // Use special conversion logic for converting to a TimeSpan
                        return(_ToTimeSpan(value));
                    }
                    else if (targetType == typeof(bool))
                    {
                        // Use special conversion logic for converting to Boolean
                        return(_ToBool(value));
                    }

                    // ---------------------------------------------------------------------------
                    //  Invoke IConvertible implementation, if any
                    // ---------------------------------------------------------------------------
                    if (value is IConvertible iConvertible)
                    {
                        // Use the System.ChangeType method, which makes full use of any IConvertible implementation on the target type
                        try
                        {
                            return(System.Convert.ChangeType(value, targetType));
                        }
                        catch
                        {
                            // Ignore exception and fall back to VBConvert implementation
                        }
                    }

                    // ---------------------------------------------------------------------------
                    //  Fall back to VBConvert methods
                    // ---------------------------------------------------------------------------
                    switch (System.Convert.GetTypeCode(targetType))
                    {
                    case TypeCode.Boolean:
                        // Use custom ToBool method
                        return(_ToBool(value));

                    case TypeCode.Byte:
                        return(VBConvert.ToByte(value));

                    case TypeCode.Char:
                        return(VBConvert.ToChar(value));

                    case TypeCode.DateTime:
                        return(VBConvert.ToDate(value));

                    case TypeCode.Decimal:
                        return(VBConvert.ToDecimal(value));

                    case TypeCode.Double:
                        return(VBConvert.ToDouble(value));

                    case TypeCode.Int16:
                        return(VBConvert.ToShort(value));

                    case TypeCode.Int32:
                        return(VBConvert.ToInteger(value));

                    case TypeCode.Int64:
                        return(VBConvert.ToLong(value));

                    case TypeCode.SByte:
                        return(VBConvert.ToSByte(value));

                    case TypeCode.Single:
                        return(VBConvert.ToSingle(value));

                    case TypeCode.String:
                        return(VBConvert.ToString(value));

                    case TypeCode.UInt16:
                        return(VBConvert.ToUShort(value));

                    case TypeCode.UInt32:
                        return(VBConvert.ToUInteger(value));

                    case TypeCode.UInt64:
                        return(VBConvert.ToULong(value));
                    }

                    // ---------------------------------------------------------------------------
                    //  Fall back to VBConvert.ChangeType
                    // ---------------------------------------------------------------------------
                    return(VBConvert.ChangeType(value, targetType));
                }
                catch (Exception ex)
                {
                    // Conversion of non-empty value to value type failed.
                    if (ignoreError)
                    {
                        return(defaultValue ?? Activator.CreateInstance(targetType));
                    }
                    throw new InvalidCastException($"Cannot convert {_FormatValue(value)} to type {targetType.FullName}", ex);
                }
            } // end IsValueType
            else
            {
                // Reference types...not much to do here besides check for empty values

                if (ValueInspection.IsNull(value, options))
                {
                    return(null);
                }

                try
                {
                    //  Fall back to VBConvert.ChangeType
                    return(VBConvert.ChangeType(value, targetType));
                }
                catch
                {
                    if (ignoreError)
                    {
                        return(defaultValue ?? null);
                    }
                    throw;
                }
            }
        }