Archives

Anticipation

  • No dates present

Clever Conversion

I’ve been planning to post this (and a few other things) for a while now, but never quite seemed to get around to it. Well, brace yourselves, because here it finally is! (And it might even turn into an actual series!)

This first part is pretty straightforward; it’s a helper class designed to make a best-effort attempt to convert any object into a specific type, by making use of any defined converters. It’s the sort of thing you’d think Convert.ChangeType would do (but doesn’t, because that only works for the native value types), and what the WPF XAML parser actually does do (although unfortunately it doesn’t seem to expose its underlying mechanism for external use; at least not that I could find).

But let’s get straight into the code:

    public static class ConversionUtilities
    {
        /// <summary>Convert the given value to the specified type (if possible).  Requires a direct <see cref="TypeConverter"/> conversion to work.</summary>
        /// <param name="currentValue">The value to be converted.</param>
        /// <param name="expectedType">The type to convert the value to.</param>
        /// <returns>The converted value; or the original value if the conversion is not necessary or not possible.</returns>
        /// <exception cref="Exception">Any exceptions thrown by the conversion operation will be propagated outwards.</exception>
        public static object Convert(object currentValue, Type expectedType)
        {
            var convertedValue = currentValue;
            if (convertedValue == null) { return convertedValue; }
            if (expectedType.IsAssignableFrom(convertedValue.GetType())) { return convertedValue; }
 
            var converter = TypeDescriptor.GetConverter(expectedType);
            if (converter != null && converter.CanConvertFrom(convertedValue.GetType()))
            {
                convertedValue = converter.ConvertFrom(convertedValue);
            }
            else
            {
                converter = TypeDescriptor.GetConverter(convertedValue.GetType());
                if (converter != null && converter.CanConvertTo(expectedType))
                {
                    convertedValue = converter.ConvertTo(convertedValue, expectedType);
                }
            }
            return convertedValue;
        }
 
        /// <summary>Convert the given value to the specified type (if possible).  Requires a direct <see cref="TypeConverter"/> conversion to work.</summary>
        /// <param name="currentValue">The value to be converted.</param>
        /// <typeparam name="T">The type to convert the value to.</typeparam>
        /// <returns>The converted value.</returns>
        /// <exception cref="InvalidCastException">Thrown if the specified conversion is not defined.</exception>
        /// <exception cref="Exception">Any exceptions thrown by the conversion operation will be propagated outwards.</exception>
        public static T Convert<T>(object currentValue)
        {
            if (currentValue == null) { return default(T); }
            return (T)Convert(currentValue, typeof(T));
        }
 
        /// <summary>Compares two objects for equality, applying conversions (if possible) if their types differ.</summary>
        /// <param name="a">The first object to compare.</param>
        /// <param name="b">The second object to compare.</param>
        /// <returns>True if the objects are convertible to an equal value.</returns>
        public static bool EqualsWithConversion(object a, object b)
        {
            if (a == null && b == null) { return true; }
            if (a == null || b == null) { return false; }
 
            if (Equals(a, b)) { return true; }
            if (Equals(Convert(a, b.GetType()), b)) { return true; }
            if (Equals(a, Convert(b, a.GetType()))) { return true; }
            return false;
        }
    }

Note that if the conversion is not possible, then the first method will return the original object, which will result in the second method throwing an InvalidCastException.

These methods only support a single conversion step — if Foo and Bar are both convertible to String (as all objects are, ultimately), then it will let you convert either a Foo object or a Bar object to a string (or vice versa, if the reverse conversion has been defined), but it will not let you convert a Foo object into a Bar object directly — although you can request the intermediate conversion yourself if that’s what you really want:

ConversionUtilities.Convert<Bar>(ConversionUtilities.Convert<string>(foo))

I chose this limitation both to help avoid potential loops if complex conversion paths are available, and because letting it chain through more than one conversion step at a time could easily lead to a loss of information or other surprising behaviour — especially since every object is convertible to a string (though not necessarily back from a string), but those conversions aren’t necessarily all that helpful.

We’ll cover a particularly interesting use for this class in a later post.

2 comments to Clever Conversion


  • Catchable fatal error: Object of class WP_Comment could not be converted to string in /home/uecasm/public_html/lambert/wpdata/wp-content/themes/atahualpa/functions/bfa_custom_comments.php on line 16