read

It has been a long time since I wrote anything at all in my blog and I kind of missed it! So here we go! This post is going to be about generics (again)! I wanted to create a method that converts a set of strings (attributes of an element, xml) and did indeed create one with the help of generics. But that’s when I discovered IConvertible! Let me start with the very simple method that would take care of the conversion.

private static T Convert<T>(object originalValue, T defaultValue)
{
    if (originalValue == null)
        return defaultValue;
    var result = (T) System.Convert.ChangeType(originalValue, typeof (T));
    return result;
}

This method takes in 2 parameters, first is the object that has to be converted and the second is the default value if the first parameter is null. If the first parameter is not null, I use Convert.ChangeType to convert the object passed to the expected type. It works and that’s great! Before we move further, here is an example of how this is used.

public static void Main(string[] args)
{
    var data = "false";
    var convertedString = Convert<bool>(data, !default(bool));
    Console.WriteLine(convertedString);

    Int16 num = 18;
    var convertedInt = Convert<Int16>(num, 0);
    Console.WriteLine(convertedInt);
}

Lets assume Convert<T> is called with an invalid value for the type passed, like this:

public static void Main(string[] args)
{
    var convertedInt = Convert<Int16>("abcd", 0);
    Console.WriteLine(convertedInt);
}

When you run the above code listing, it will fail catastrophically! Do I even have to explain why? Guess not! So I need to make Convert “safe”. Here is an implementation of the same with the help of IConvertible.

public class ConvertibleObject : IConvertible
{
    private readonly object _srcValue;
    private readonly object _defaultValue;

    public ConvertibleObject(object srcValue, object defaultValue)
    {
        _srcValue = srcValue;
        _defaultValue = defaultValue;
    }

    public TypeCode GetTypeCode()
    {
        return TypeCode.Object;
    }

    public bool ToBoolean(IFormatProvider provider)
    {
        return SafeConvert<bool>();
    }

    public short ToInt16(IFormatProvider provider)
    {
        return SafeConvert<Int16>();
    }

    public double ToDouble(IFormatProvider provider)
    {
        return SafeConvert<double>();
    }

    public string ToString(IFormatProvider provider)
    {
        return SafeConvert<string>();
    }

    public decimal ToDecimal(IFormatProvider provider)
    {
        return SafeConvert<decimal>();
    }

    private T SafeConvert<T>()
    {
        var srcType = typeof (T);
        var safeConverter = srcType.GetMethods(Flags)
                                   .FirstOrDefault(m => m.Name == "TryParse" &&
                                                        m.GetParameters().Count() == 2 &&
                                                        m.GetParameters().Last().IsOut);
        if (safeConverter != null)
        {
            var args = new[] { _srcValue, null };
            var result = (bool)safeConverter.Invoke(null, args);
            return result ? (T)args[1] : (T)_defaultValue;
        }
        return (T)_defaultValue;
    }

    private const BindingFlags Flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static;

    #region Unimplemented methods
    // ...
    #endregion
}

To use this, I could do the following:

public static void Main(string[] args)
{
    var conv = new ConvertibleObject("12.23", 0.0);
    decimal decimalResult = System.Convert.ToDecimal(conv);
    Console.WriteLine(decimalResult);
}

What am I doing here? Convert.ToDecimal, Convert.ToDouble etc etc all has an overload that takes in an object. To do the actual conversion, the object is cast in to an IConvertible and the ToDecimal, ToDouble etc methods are called! Yes, its as simple as that!!

But using IConvertible might be overkill! So I am just going to simplify this in to a simple stat generic method as shown below:

private static T SafeConvert<T>(object originalValue, T defaultValue)
{
    var srcType = typeof(T);
    var safeConverter = (from method in srcType.GetMethods(Flags)
			 let paramList = method.GetParameters()
			 where method.Name == "TryParse" && paramList.Count() == 2
                               && paramList.Last().IsOut
			 select method).FirstOrDefault();
    if (safeConverter != null)
    {
        var args = new[] { originalValue, null };
        var result = (bool)safeConverter.Invoke(null, args);
        return result ? (T)args[1] : defaultValue;
    }
    return defaultValue;
}

With these changes, even the following code snippet wouldn’t throw an exception at runtime!

public static void Main(string[] args)
{
    var convertedInt = Convert<Int16>("abcd", 0);
    Console.WriteLine(convertedInt);
}

One question there might be is why I have started using the from syntax instead of FirstOrDefault. That’s just so that I don’t call the GetParameters method twice!!

Okay, I guess I have made my point now! Do let me know your comments if you have any!! Happy coding!!!

Karthik


Published on

Leave a comment

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

the dotNet way!

experiments w/ asp.net, c#, ruby and many more!!

Back to Home