Convert to unknown generic type: ChangeType<T>

Problem

When you create a generic method (Foo) or generic class (Bar), it often happens that you need to change (or convert) a type to T. In these situations you need a ChangeType function that changes "any" type to T. Unfortunately this is not a standard method. The Cast() method of Linq looks like a solution but it is very limited. This wil not work for a lot of types:

                        
                                
      public static T ChangeTypeLinqVariant<T>(this object value)
      {
        return (new [] {value}).Cast<T>().Single();
      }
      
                        
                    

Examples

obvious examples where you need a ChangeType method are:

  • returns T but the parameter of the method is an object, string, XML or data from a DataReader.
  • Method returns T1 but the parameters of the method is T2 (T1 Foo(T2 value))

A less obvious example is:

                        
                                
        private void AddItem<TK, TV="">(ref Dictionary<TK, TV="">dictionary, TK key, TV value)
      {
        if (key is string)
        {
          // This wil not compile because key is a TK, not a string
          key = key.ToString().ToLower();
          // This will do the job
          key = key.ToString().ToLower().ChangeType<TK>();
        }
        if (dictionary.ContainsKey(key)) return;
        dictionary.Add(key, value);
      }
      
                        
                    

Solution

A start it the good direction was a post on StackOverflow. But that solution didn't work for types like enum's, strings, Guids, etc. So I add soms new code to it and now it works for most of the common scenarios.

                        
                                
      public static T ChangeType<T>(this object value, CultureInfo cultureInfo)
      {
        var toType = typeof(T);

        if (value == null) return default(T);

        if (value is string)
        {
          if (toType == typeof(Guid))
          {
            return ChangeType<T>(new Guid(Convert.ToString(value, cultureInfo)), cultureInfo);
          }
          if ((string)value == string.Empty && toType != typeof(string))
          {
            return ChangeType<T>(null, cultureInfo);
          }
        }
        else
        {
          if (typeof(T) == typeof(string))
          {
            return ChangeType<T>(Convert.ToString(value, cultureInfo), cultureInfo);
          }
        }

        if (toType.IsGenericType &&toType.GetGenericTypeDefinition() == typeof(Nullable<>))
        {
        toType = Nullable.GetUnderlyingType(toType); ;
        }

        bool canConvert = toType is IConvertible || (toType.IsValueType && !toType.IsEnum);
        if (canConvert)
        {
          return (T)Convert.ChangeType(value, toType, cultureInfo);
        }
        return (T)value;
      }
      </Tekst>
      <Tekst IsTekst="Ja">It might be handy to add some extra overloads like:</Tekst>
      <Tekst IsCode="Ja">
      public static T ChangeType<T>(this object value)
      {
        return ChangeType<T>(value, CultureInfo.CurrentCulture);
      }
      
                        
                    

Leave a Comment

Comment

Comments