Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert System.String generically to any complex type using "Convert.ChangeType()"

I try to generically convert user input into either simple or complex types:

class Program
{
  static void Main(string[] args)
  {
    Console.WriteLine("Welcome, please provide the following info... Confirm with <RETURN>!");
    Console.WriteLine();    

    Console.Write("Name (e.g. 'Peggy Sue'): ");
    var user = GetUserInput<User>(Console.ReadLine());

    Console.WriteLine();
    Console.WriteLine();
    Console.WriteLine("Hi {0}, nice to meet you!", user.Forename);
    Console.WriteLine();

    Console.Write("Age: ");
    user.Age = GetUserInput<ushort>(Console.ReadLine());

    Console.WriteLine();
    Console.WriteLine("Thanks and goodbye!");
    Console.WriteLine("Press <RETURN> to quit...");
    Console.ReadLine();
  }

  static T GetUserInput<T>(string data)
  {
    return (T) Convert.ChangeType(data, typeof (T));
  }
}

class User
{
  public User(string name)
  {
    var splitted = name.Split(' ');
    Forename = splitted[0];
    Lastname = splitted[1];
  }

  public static implicit operator User (string value)
  {
    return new User(value);
  }

  public static explicit operator string (User value)
  {
    return string.Concat(value.Forename, " ", value.Lastname);
  }

  public string Forename { get; private set; }
  public string Lastname { get; private set; }

  public ushort Age { get; set; }
}

For the conversion to my "User" class, I always get the exception "Invalid cast from 'System.String' to 'ConsoleApplication1.User'.". Does anyone know how to fix this?

If I try something like this (not generically), it works just perfect:

Console.WriteLine((string) ((User) "Peggy Sue"));
like image 658
Maik Avatar asked Mar 23 '11 07:03

Maik


3 Answers

No, Convert.ChangeType only works with a fixed set of types, I believe... or if the original object implements IConvertible, it can call IConvertible.ToType. That means you could implement IConvertible in your User class and have

Convert.ChangeType(user, typeof(string))

working, but that won't work the other way round.

Do you have a fixed set of types you need to convert? If so, you could have a Dictionary<Type, Func<string, object>> which you'd populate with conversion delegates. Then you just need to call the appropriate conversion and cast the return value. It's ugly, but probably your best bet.

like image 179
Jon Skeet Avatar answered Oct 16 '22 00:10

Jon Skeet


One option here might be to associate a TypeConverter with the types you care about (you can do this at compile-time via [TypeConverter(...)], or there is a trick for doing this at runtime if you don't control the types).

Then it is:

TypeConverter conv = TypeDescriptor.GetConverter(typeof(T));
T obj = (T)conv.ConvertFromString(text); // or ConvertFromInvariantString
like image 25
Marc Gravell Avatar answered Oct 16 '22 00:10

Marc Gravell


I fixed it. Check this:

class Program
{
  static void Main(string[] args)
  {
    Console.WriteLine("Welcome, please provide the following info... Confirm with <RETURN>!");
    Console.WriteLine();

    Console.Write("Name (e.g. 'Peggy Sue'): ");
    var user = GetUserInput<User>(Console.ReadLine());

    Console.WriteLine();
    Console.WriteLine();
    Console.WriteLine("Hi {0}, nice to meet you!", user.Forename);
    Console.WriteLine();

    Console.Write("Age: ");
    user.Age = GetUserInput<ushort>(Console.ReadLine());

    Console.WriteLine();
    Console.WriteLine("Thanks and goodbye!");
    Console.WriteLine("Press <RETURN> to quit...");
    Console.ReadLine();
  }

  static T GetUserInput<T>(string data)
  {
    TypeConverter conv = TypeDescriptor.GetConverter(typeof(T));
    return (T) conv.ConvertFromInvariantString(data);
  }
}

[TypeConverter(typeof(UserConverter))]
class User
{
  public User(string name)
  {
    var splitted = name.Split(' ');
    Forename = splitted[0];
    Lastname = splitted[1];
  }

  public static explicit operator User (string value)
  {
    return new User(value);
  }

  public static explicit operator string (User value)
  {
    return string.Concat(value.Forename, " ", value.Lastname);
  }

  public string Forename { get; private set; }
  public string Lastname { get; private set; }

  public ushort Age { get; set; }
}

class UserConverter : TypeConverter
{
  public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
  {
    return (typeof(string) == sourceType);
  }

  public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
  {
    if (value is string)
    {
      return (User)(value as string);
    }

    return null;
  }
}
like image 22
Maik Avatar answered Oct 16 '22 00:10

Maik