Click here to Skip to main content
15,867,686 members
Articles / Programming Languages / C#

Using LINQ for type Conversion

Rate me:
Please Sign up or sign in to vote.
5.00/5 (15 votes)
25 Feb 2010CPOL 52.9K   32   18
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.

C#
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:

C#
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)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Jörgen Andersson14-Nov-12 2:24
professionalJörgen Andersson14-Nov-12 2:24 
GeneralI cant get a simple example to build Pin
theperm9-Feb-11 5:55
theperm9-Feb-11 5:55 
GeneralRe: I cant get a simple example to build Pin
dasblinkenlight9-Feb-11 6:28
dasblinkenlight9-Feb-11 6:28 
Generalgood one - have 5 from me Pin
Pranay Rana19-Dec-10 0:30
professionalPranay Rana19-Dec-10 0:30 
GeneralInteresting thing... Pin
Paulo Zemek26-Feb-10 6:17
mvaPaulo Zemek26-Feb-10 6:17 
GeneralRe: Interesting thing... Pin
dasblinkenlight26-Feb-10 11:23
dasblinkenlight26-Feb-10 11:23 
QuestionWhat am I missing? Pin
Josh Fischer25-Feb-10 8:59
Josh Fischer25-Feb-10 8:59 
AnswerRe: What am I missing? Pin
dasblinkenlight25-Feb-10 9:31
dasblinkenlight25-Feb-10 9:31 
GeneralRe: What am I missing? Pin
Josh Fischer25-Feb-10 14:05
Josh Fischer25-Feb-10 14:05 
AnswerRe: What am I missing? Pin
dasblinkenlight25-Feb-10 18:30
dasblinkenlight25-Feb-10 18:30 
GeneralRe: What am I missing? Pin
Josh Fischer26-Feb-10 4:18
Josh Fischer26-Feb-10 4:18 
GeneralGetHashCode and the secrets of multiplying by 31 Pin
Marc Brooks1-Mar-10 13:51
Marc Brooks1-Mar-10 13:51 
GeneralRe: What am I missing? Pin
PIEBALDconsult26-Feb-10 3:23
mvePIEBALDconsult26-Feb-10 3:23 
AnswerRe: What am I missing? Pin
dasblinkenlight26-Feb-10 5:47
dasblinkenlight26-Feb-10 5:47 
GeneralInteresting, but... Pin
Paulo Zemek25-Feb-10 8:57
mvaPaulo Zemek25-Feb-10 8:57 
GeneralRe: Interesting, but... Pin
dasblinkenlight25-Feb-10 9:45
dasblinkenlight25-Feb-10 9:45 
GeneralRe: Interesting, but... [modified] Pin
Paulo Zemek25-Feb-10 10:34
mvaPaulo Zemek25-Feb-10 10:34 
Are you sure about how fast it is? I didn't made the test, but consider:

Convert.ChangeType, as far as I know, is already implemented to be as fast as it can. But, you use a dictionary. Ok, when you only try int? to int, the dictionary lookup is very fast... but when you have already converted int -> double
double -> float
double ->int
int -> double?
int? -> double

And so on... it really keeps that advantage?
Also, the Synchronized attribute you used means locking. So, if you use a multi-core machine (say, 8 CPUs) the Convert.ChangeType will be able to run in all cores without problems (for example, when many threads are processing database-data), where the lookup to the dictionary will run one at a time (even if the conversion itself is faster) and will clear the CPU caches... I can't imagine it will keep that advantage.

But, as I said earlier, it is a very interesting use of the expressions trees. I think you will be one of the first to use the new expressions trees in .Net 4.0, where you can create full methods using them.

* As final notes, I think that if your PairOfTypes is a struct it will be even faster and Convert.ChangeType can convert to and from strings.
modified on Thursday, February 25, 2010 4:41 PM

GeneralRe: Interesting, but... Pin
dasblinkenlight25-Feb-10 18:01
dasblinkenlight25-Feb-10 18:01 

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

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