views:

644

answers:

9

What I want to do is:

bool Convert( out Object output, Object source)
{
   // find type of output.
   // convert source to that type if possible
   // store result in output.

   return success
}

Is it possible?

Obviously, there is a brute force massive "if" construct that could work, but that would require writing an if block for every conceivable data type. Even assuming we'll limit it to primitives and strings, it's still a huge chunk of code. I'm thinking about something a bit more reflective.

Aside: While going through the api, I came across the Convert.IsDBNull() method, which will save me a lot of

 if ( !databasefield.GetType().Equals( DBNull.Value ) )

Why in the name of G-d is it in Convert? Why not DBNull.IsDBNull() ?

A: 

Try using generics. That way you don't have to do all of the run-time type checking that you would otherwise have to do (it's all done at compile time, instead).

Brian
+2  A: 

I've ran across your question many times. I've always found that the time to build and use the conversion function negates the time it saves. Eventually problems like precision and rounding occur and you still have to handle special cases.

For checking for dbnull... I use typeof (object) is DbNull ...

Sam
+3  A: 

Here is a sample that I use, you can inject other complex conversions into it by registering other type converters.

public static class Converter
{
    public static T Convert<T>(object obj, T defaultValue)
    {
        if (obj != null)
        {
            if (obj is T)
            {
                return (T)obj;
            }

            TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));

            if (converter.CanConvertFrom(obj.GetType()))
            {
                return (T)converter.ConvertFrom(obj);
            }
        }

        return defaultValue;
    }
Brian Rudolph
This won't work in most cases since not many types have converters that converts to and from other types than string.
Patrik Hägne
Which is why i said you can register other converters.
Brian Rudolph
Yes, you said that for complex conversions, but this doesn't even handle simple conversions like int to string.
Patrik Hägne
@Patrik, if you want primitive conversions, use the Convert classes.
Robert Paulson
A: 

Even with Convert.IsDBNull, there's a much better way of doing your check:

if (!databaseField is DBNull)

Also be aware that you can use == on Type as there's only ever one Type instance for a particular type.

Jon Skeet
I'm aware of the == issue. However coming from a Java background, I've just never been comfortable using it.I've also got a phobia around "is" or "instanceof" I just don't trust binary (two-operand) keywords.
chris
You can use == with Class<T> in Java as well - again, because there's only ever one object for a class representation. I strongly recommend you try hard to get over your phobia. Why should you not "trust" binary keyword operators? Your code is suffering because of this.
Jon Skeet
A: 

Have you seen this function?:

Microsoft.VisualBasic.CType()
Joel Coehoorn
Ctype is not a function, is a keyword, so you cannot use it in other languages (the OP seems be asking for C#). The compiler replaces it with an appropriate conversion(usually Microsoft.VisualBasic.Conversions.* for primitive types).
ggf31416
+2  A: 

A fellow dev wrote exactly this function, which we find extremely useful.

Essentially, it uses reflection to search for an implicit conversion between the two types (search for "op_Implicit" for more info).

Failing that, it searches for an Constructor of the destination-type that takes the source-type as a parameter, and calls that.

Failing that, it searches for a Parse method that can parse one type into another. This is will find things like Int32.Parse to convert from String to Int, or IPAddress.Parse to convert from String to IPAddress.

As a performance optimization, once it finds a conversion once, it keeps it in a dictionary of [type,type] <==> [conversion MethodInfo] so that subsequent calls don't have to go through an extensive reflection search.

This handles almost all type-conversions quite nicely.

abelenky
+2  A: 

This was a fun little exercise! I just wrote this so don't hang me if it doesn't work but here I try converting with the different methods I can think of right now.

public static class Converter
{
    public static bool TryConvert<T>(object o, out T result)
    {
        if (o == null && typeof(T).IsClass)
        {
            result = default(T);
            return true;
        }

        var convertible = o as IConvertible;
        if (convertible != null && ConvertibleHandlesDestinationType<T>())
        {
            result = (T)Convert.ChangeType(convertible, typeof(T));
            return true;
        }

        if (o != null)
        {
            if (typeof(T).IsAssignableFrom(o.GetType()))
            {
                result = (T)o;
                return true;
            }

            var converter = TypeDescriptor.GetConverter(o);
            if (converter.CanConvertTo(typeof(T)))
            {
                result = (T)converter.ConvertTo(o, typeof(T));
                return true;
            }
        }

        result = default(T);
        return false;
    }

    private static bool ConvertibleHandlesDestinationType<T>()
    {
        return 
            typeof(T).Equals(typeof(Boolean)) ||
            typeof(T).Equals(typeof(Byte)) ||
            typeof(T).Equals(typeof(char)) ||
            typeof(T).Equals(typeof(DateTime)) ||
            typeof(T).Equals(typeof(Decimal)) ||
            typeof(T).Equals(typeof(Double)) ||
            typeof(T).Equals(typeof(Int16)) ||
            typeof(T).Equals(typeof(Int32)) ||
            typeof(T).Equals(typeof(Int64)) ||
            typeof(T).Equals(typeof(SByte)) ||
            typeof(T).Equals(typeof(Single)) ||
            typeof(T).Equals(typeof(string)) ||
            typeof(T).Equals(typeof(UInt16)) ||
            typeof(T).Equals(typeof(UInt32)) ||
            typeof(T).Equals(typeof(UInt64));
    }
}

Since the output parameter is of the type T, we are able to use type inference so that the calls would look like:

int number;
if (Converter.TryConvert("123", out number))
{
    Debug.WriteLine(number);
}
Patrik Hägne
If your going to follow the syntax of DateTime.TryParse and similar, I'd recommend wrapping your entire method in a try/catch and then have the out default(T) return false since the TryParse methods are under the expectation they will never throw an exception.
Chris Marisic
A: 

I wrote a blog post on how I manage conversion of types from a DataRow on my blog Working with DataTables/Datarows

///<summary>
/// Extension methods for manipulating DataRows
///</summary>
public static class DataRowUserExtensions
{
    /// <summary>
    /// Determines whether [is null or empty string] [the specified data row].
    /// </summary>
    /// <param name="dataRow">The data row.</param>
    /// <param name="key">The key.</param>
    /// <returns>
    ///   <c>true</c> if [is null or empty string] [the specified data row]; otherwise, <c>false</c>.
    /// </returns>
    public static bool IsNullOrEmptyString(this DataRow dataRow, string key)
    {
        if (dataRow.Table.Columns.Contains(key))
            return dataRow[key] == null || dataRow[key] == DBNull.Value || dataRow[key].ToString() == string.Empty;

        throw new ArgumentOutOfRangeException(key, dataRow, "does not contain column");
    }

    /// <summary>
    /// Gets the specified data row.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="dataRow">The data row.</param>
    /// <param name="key">The key.</param>
    /// <returns></returns>
    public static T Get<T>(this DataRow dataRow, string key)
    {
        if (dataRow.Table.Columns.Contains(key))
            return dataRow.IsNullOrEmptyString(key) ? default(T) : (T) ChangeTypeTo<T>(dataRow[key]);

        throw new ArgumentOutOfRangeException(key, dataRow, "does not contain column");
    }

    /// <summary>
    /// Changes the type to.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="value">The value.</param>
    /// <returns></returns>
    private static object ChangeTypeTo<T>(this object value)
    {
        if (value == null)
            return null;

        Type underlyingType = typeof (T);
        if (underlyingType == null)
            throw new ArgumentNullException("value");

        if (underlyingType.IsGenericType && underlyingType.GetGenericTypeDefinition().Equals(typeof (Nullable<>)))
        {
            var converter = new NullableConverter(underlyingType);
            underlyingType = converter.UnderlyingType;
        }

        // Guid convert
        if (underlyingType == typeof (Guid))
        {
            return new Guid(value.ToString());
        }

        // Do conversion
        return underlyingType.IsAssignableFrom(value.GetType()) ?
              Convert.ChangeType(value, underlyingType)
            : Convert.ChangeType(value.ToString(), underlyingType);
    }
}
Chris Marisic
A: 

There is no holy grail of conversion. For m types you would need m*(m-1) conversion routines to cover all the permutations.

For primitive types, use Convert.ChangeType

If a type is convertable to-from a primitive, it can implement the IConvertable interface and be used from the Convert class.

For everything else, @Brian Rudolfs answer is the best. Register an explicit conversion method for each permutation you require.

Robert Paulson