/// <summary> /// Performs custom conversion for enumeration values to allow flagged enumerators to be converted from, using compound numeric values or delimited strings /// </summary> /// <typeparam name="T"> /// The data type which the specified value should be converted to, if possible. /// </typeparam> /// <param name="value"> /// The value to be converted /// </param> /// <param name="result"> /// The converted equivelent of the specified <i>value</i>, or the default value for the specified type, <i>T</i>, where conversion has failed. /// </param> /// <returns> /// A <see cref="T:System.Boolean" /> value which indicates if conversion succeeded /// </returns> private static Boolean AttemptEnumeratorConversion <T>(Object value, out T result) { Boolean success; // Get the type or, for nullable types, the underlying type Type underlyingType = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T); if (value == null || value == DBNull.Value) { // Conversion failed, no value provided result = default(T); success = false; } else if (!underlyingType.IsEnum) { // Conversion failed, type is not an enumerator result = default(T); success = false; } else if (value is Guid) { result = default(T); success = false; // Loop through each defined enumeration and compare the enumeration guid with the guid specified. foreach (T enumeration in Enum.GetValues(underlyingType)) { EnumerationGuid guid = underlyingType.GetField(enumeration.ToString(), BindingFlags.Public | BindingFlags.Static).GetCustomAttributes <EnumerationGuid>().FirstOrDefault(); if (guid != null && Guid.Equals(guid.Guid, value)) { result = enumeration; success = true; break; } } } else if (value is String) { success = true; Int64 checkedValue = 0; // Remove leading and trailing spaces from each element and separate by multiple known delimiters (commas, pipes, etc) IEnumerable <String> stringValueCollection = value.ToString().Split(Conversion.ENUMERATOR_DELIMITERS, StringSplitOptions.RemoveEmptyEntries).Select(val => val.Trim()); foreach (String stringValue in stringValueCollection) { // Get the enumeration which matches the string value (case-insensitive) IEnumerable <T> enumeration = Enum.GetValues(underlyingType).OfType <T>().Where(enumValue => String.Equals(stringValue, Enum.GetName(underlyingType, enumValue), StringComparison.OrdinalIgnoreCase)); if (enumeration.Count() == 0) { success = false; } else { checkedValue |= Convert.ToInt64(enumeration.First()); } } // Convert the compounded result to the enumerators underlying type, if all enumeration values were converted successfully if (success) { result = (T)Convert.ChangeType(checkedValue, Enum.GetUnderlyingType(underlyingType)); } else { result = default(T); } } else if (value is Byte || value is SByte || value is Int16 || value is UInt16 || value is Int32 || value is UInt32 || value is Int64 || value is UInt64) { Int64 checkedValue = 0; Int64 numeralValue = Convert.ToInt64(value); // Test the specified numeric value against each enumeration value and compound the value to the result foreach (Object enumeration in Enum.GetValues(underlyingType)) { Int64 enumValue = Convert.ToInt64(enumeration); if ((numeralValue & enumValue) == enumValue) { checkedValue |= enumValue; } } // Convert the compounded result to the enumerators underlying type result = (T)Enum.ToObject(underlyingType, Convert.ChangeType(checkedValue, Enum.GetUnderlyingType(underlyingType))); success = true; } else { // Conversion failure: value is neither a string nor a numeric type result = default(T); success = false; } return(success); }
/// <summary> /// Converts the specified <paramref name="value" /> to the specified data type, falling back to a default value where conversion cannot be achieved /// </summary> /// <typeparam name="T"> /// The data type which the specified value should be converted to, if possible. /// </typeparam> /// <param name="value"> /// The value to be converted /// </param> /// <param name="defaultValue"> /// The default value to be returned if conversion cannot be achieved /// </param> /// <returns> /// Returns the converted value or, where conversion fails, the specified default value /// </returns> private static T AttemptConversion <T>(Object value, T defaultValue) { // Defines the value to be returned. This local variable is uninitialised to ensure that all scenarios have been catered for within this method, // as a compiler warning will be generated (for using an uninitialised local variable) if any scenarios is missed. T returnValue; T parsedValue; // Identify the absolute type (for System.Nullable wrapped types) Type absoluteType = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T); Boolean isNumeric = (value != null && value != DBNull.Value) && (value.GetType().IsEnum || value.GetType() == typeof(SByte) || value.GetType() == typeof(Int16) || value.GetType() == typeof(Int32) || value.GetType() == typeof(Int64) || value.GetType() == typeof(Decimal) || value.GetType() == typeof(Double) || value.GetType() == typeof(Single) || value.GetType() == typeof(Byte) || value.GetType() == typeof(UInt16) || value.GetType() == typeof(UInt32) || value.GetType() == typeof(UInt64)); // Check if the value is null (or a null-equivelent) if (value == null || (Object)(value) == DBNull.Value) { // If the specified type is a value type, then null cannot be converted because value types (structs) are non-nullable if (typeof(T).IsValueType) { // Conversion failed - value type does not allow null value returnValue = defaultValue; } else { returnValue = (T)(Object)null; } } // Perform boxing if specified value is already of the required type or can be assigned to the specified type else if (value != null && (value is T || typeof(T).IsAssignableFrom(value.GetType()))) { returnValue = (T)value; } // If conversion can be achieved dynamically, using a custom-conversion method, perform the conversion and return the result of the conversion else if (Conversion.AttemptCustomConversion <T>(value, out parsedValue)) { returnValue = parsedValue; } // If the specified type is an enumerator, invoke custom conversion logic to perform the conversion else if (Conversion.AttemptEnumeratorConversion <T>(value, out parsedValue)) { returnValue = parsedValue; } // If conversion can be achieved dynamically, using a standard Microsoft.NET method, perform the conversion and return the result of the conversion else if (Conversion.AttemptStandardConversion <T>(value, out parsedValue)) { returnValue = parsedValue; } else if (absoluteType == typeof(Guid) && value.GetType().IsEnum) { EnumerationGuid guid = value.GetType().GetField(value.ToString(), BindingFlags.Public | BindingFlags.Static).GetCustomAttributes().OfType <EnumerationGuid>().FirstOrDefault(); if (guid != null) { returnValue = (T)(Object)guid.Guid; } else { returnValue = defaultValue; } } // If converting to a string, use the ToString method with a predefined formatting standard. For enumerators, use the value of the description attribute else if (typeof(T) == typeof(String)) { if (value is Enum) { returnValue = (T)(Object)(value.GetType().GetField(value.ToString(), BindingFlags.Public | BindingFlags.Static).GetCustomAttributes(true).OfType <DescriptionAttribute>().Select(attr => attr.Description).FirstOrDefault() ?? value.ToString()); } else if (value is Int16 || value is Int32 || value is Int64) { returnValue = (T)(Object)Convert.ToInt64(value).ToString("0"); } else if (value is UInt16 || value is UInt32 || value is UInt64) { returnValue = (T)(Object)Convert.ToUInt64(value).ToString("0"); } else if (value is Decimal) { returnValue = (T)(Object)((Decimal)value).ToString("0.00"); } else if (value is Double) { returnValue = (T)(Object)((Double)value).ToString("0.00"); } else if (value is Single) { returnValue = (T)(Object)((Single)value).ToString("0.00"); } else if (value is DateTime) { returnValue = (T)(Object)((DateTime)value).ToString("dd MMMM yyyy"); } else if (value is TimeSpan) { returnValue = (T)(Object)((TimeSpan)value).ToString("HH:mm"); } else if (value is Boolean) { returnValue = (T)(Object)((Boolean)value ? "Yes" : "No"); } else { returnValue = (T)(Object)value.ToString(); } } // Attempt conversion from one numeral type to another numeral type else if (isNumeric && absoluteType == typeof(SByte)) { try { returnValue = (T)(Object)Convert.ToSByte(value); } catch { returnValue = defaultValue; } } else if (isNumeric && absoluteType == typeof(Int16)) { try { returnValue = (T)(Object)Convert.ToInt16(value); } catch { returnValue = defaultValue; } } else if (isNumeric && absoluteType == typeof(Int32)) { try { returnValue = (T)(Object)Convert.ToInt32(value); } catch { returnValue = defaultValue; } } else if (isNumeric && absoluteType == typeof(Int64)) { try { returnValue = (T)(Object)Convert.ToInt64(value); } catch { returnValue = defaultValue; } } else if (isNumeric && absoluteType == typeof(Decimal)) { try { returnValue = (T)(Object)Convert.ToDecimal(value); } catch { returnValue = defaultValue; } } else if (isNumeric && absoluteType == typeof(Double)) { try { returnValue = (T)(Object)Convert.ToDouble(value); } catch { returnValue = defaultValue; } } else if (isNumeric && absoluteType == typeof(Single)) { try { returnValue = (T)(Object)Convert.ToSingle(value); } catch { returnValue = defaultValue; } } else if (isNumeric && absoluteType == typeof(Byte)) { try { returnValue = (T)(Object)Convert.ToByte(value); } catch { returnValue = defaultValue; } } else if (isNumeric && absoluteType == typeof(UInt16)) { try { returnValue = (T)(Object)Convert.ToUInt16(value); } catch { returnValue = defaultValue; } } else if (isNumeric && absoluteType == typeof(UInt32)) { try { returnValue = (T)(Object)Convert.ToUInt32(value); } catch { returnValue = defaultValue; } } else if (isNumeric && absoluteType == typeof(UInt64)) { try { returnValue = (T)(Object)Convert.ToUInt64(value); } catch { returnValue = defaultValue; } } else if (absoluteType == typeof(Byte)) { try { returnValue = value.ToString() == "True" ? (T)(Object)Convert.ToByte(1) : (T)(Object)Convert.ToByte(0); } catch { returnValue = defaultValue; } } // If all conversion attempts have failed, fall back to the specified default value else { returnValue = defaultValue; } return(returnValue); }