Click here to Skip to main content
Click here to Skip to main content

Using LINQ for type conversion

, 25 Feb 2010 CPOL
Rate this:
Please Sign up or sign in to vote.
Converting between types in .NET.

Introduction

.NET provides several ways to change the type of a value to another type at run-time. Each technique has its limitations. This article provides (yet) another alternative that works in all instances when the CLR is capable of performing a type cast.

Background

Two popular solutions to the problem of type conversion are to use System.Convert.ChangeType, or to obtain System.ComponentModel.TypeConverter and call its ConvertFrom method. The first method breaks when you try converting a value type T to System.Nullable<T>; the second one breaks when you try converting different numeric types, for example, float to double. These limitations appear especially frustrating, because the CLR has built-in capabilities to perform both types of conversion.

One way of using these type casting capabilities is to build a LINQ lambda expression, compile it into a Func<object,object>, and then use the compiled delegate every time we need to convert between two types.

Using the code

The code below implements this approach, wrapping it into an extension method of System.Type.

public static class TypeCast {
    // This is the method exposing the rest of the functionality
    public static object Cast(this Type type, object obj) {
        return GetConverter(type, obj)(obj);
    }
    private static readonly IDictionary<PairOfTypes,Func<object,object>> converters =
        new Dictionary<PairOfTypes,Func<object,object>>();
    private static readonly ParameterExpression convParameter =
        Expression.Parameter(typeof(object), "val");
    // This is the method with the "guts" of the implementation
    [MethodImpl(MethodImplOptions.Synchronized)]
    private static Func<object,object> GetConverter(Type targetType, object val) {
        var fromType = val != null ? val.GetType() : typeof(object);
        var key = new PairOfTypes(fromType, targetType);
        Func<object,object> res;
        if (converters.TryGetValue(key, out res)) {
            return res;
        }
        res = (Func<object,object>)Expression.Lambda(
            Expression.Convert(
                Expression.Convert(
                    Expression.Convert(
                        convParameter
                    ,   fromType
                    )
                ,   targetType
                )
            ,   typeof(object)
            )
        ,   convParameter
        ).Compile();
        converters.Add(key, res);
        return res;
    }
    // This class provides Equals and GetHashCode
    // for a pair of System.Type objects.
    private class PairOfTypes {
        private readonly Type first;
        private readonly Type second;
        public PairOfTypes(Type first, Type second) {
            this.first = first;
            this.second = second;
        }
        public override int GetHashCode() {
            return 31*first.GetHashCode() + second.GetHashCode();
        }
        public override bool Equals(object obj) {
            if (obj == this) {
                return true;
            }
            var other = obj as PairOfTypes;
            if (other == null) {
                return false;
            }
            return first.Equals(other.first)
                && second.Equals(other.second);
        }
    }
}

Now, you can use the Cast method like this:

double? x = typeof(double?).Cast(1.0);
int y = typeof(int).Cast(1.2345);

Happy coding!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

dasblinkenlight

United States United States
No Biography provided

Comments and Discussions

 
GeneralMy vote of 5 PinmemberJörgen Andersson14-Nov-12 3:24 
GeneralI cant get a simple example to build Pinmembertheperm9-Feb-11 6:55 
GeneralRe: I cant get a simple example to build Pinmemberdasblinkenlight9-Feb-11 7:28 
Generalgood one - have 5 from me PinmemberPranay Rana19-Dec-10 1:30 
GeneralInteresting thing... PinmemberPaulo Zemek26-Feb-10 7:17 
GeneralRe: Interesting thing... Pinmemberdasblinkenlight26-Feb-10 12:23 
QuestionWhat am I missing? PinmvpJosh Fischer25-Feb-10 9:59 
AnswerRe: What am I missing? Pinmemberdasblinkenlight25-Feb-10 10:31 
GeneralRe: What am I missing? PinmvpJosh Fischer25-Feb-10 15:05 
AnswerRe: What am I missing? Pinmemberdasblinkenlight25-Feb-10 19:30 
GeneralRe: What am I missing? PinmvpJosh Fischer26-Feb-10 5:18 
dasblinkenlight wrote:
it produces a delegate corresponding to the expression of the lambda

I know how lambdas work, lol Wink | ;)
dasblinkenlight wrote:
perform casts in the same way it does casts in the regular compiled .NET code

This is what I wasn't seeing; it's so simple! (slaps head) WTF | :WTF:
I put together a sample that makes it more clear; well to me anyway.
public class TestClass<T,U>
{
    public T Test(U val)
    {
        return (T)typeof(T).Cast(val);
    }
    public object Test2(object val)
    {
        return typeof(T).Cast(val);
    }
    public T Test3(U val)
    {
        return (T)Convert.ChangeType(val, typeof(T));
    }
    public object Test4(object val)
    {
        return Convert.ChangeType(val, typeof(T));
    }
}
TestClass<int?,int> nullTest = new TestClass<int?,int>();
var foo = nullTest.Test(5);
var foo2 = nullTest.Test2(5);
bool bar = foo2 is Nullable<int>;
//var foo3 = nullTest.Test3(5); // fails
//var foo4 = nullTest.Test4(5); // fails
//int foo5 = 5D; // compile time error
int foo5 = (int)5D;
TestClass<int, double> cheat = new TestClass<int, double>();
int foo6 = cheat.Test(5D);
var foo7 = cheat.Test2(5D);
bool bar2 = foo7 is int;
dasblinkenlight wrote:
I parse textual representations of expressions

I've actually been doing some work around this area too (that's why I'm so interested in understanding your solution). Needless to say, I am disappointed with 3.5's expression support, but 4.0 looks very exciting (assignments!).
There are also some great articles here on codeproject where people have parsed all kinds of text based expressions and converted them to every format you can imagine. One in particular spits everything directly into IL; crazy.
Josh Fischer

GeneralGetHashCode and the secrets of multiplying by 31 PinmemberMarc Brooks1-Mar-10 14:51 
GeneralRe: What am I missing? PinmvpPIEBALDconsult26-Feb-10 4:23 
AnswerRe: What am I missing? Pinmemberdasblinkenlight26-Feb-10 6:47 
GeneralInteresting, but... PinmemberPaulo Zemek25-Feb-10 9:57 
GeneralRe: Interesting, but... Pinmemberdasblinkenlight25-Feb-10 10:45 
GeneralRe: Interesting, but... [modified] PinmemberPaulo Zemek25-Feb-10 11:34 
GeneralRe: Interesting, but... Pinmemberdasblinkenlight25-Feb-10 19:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.141223.1 | Last Updated 25 Feb 2010
Article Copyright 2010 by dasblinkenlight
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid