Преобразуйте System.String в общем случае в любой сложный тип, используя «Convert.ChangeType ()» - PullRequest
5 голосов
/ 23 марта 2011

Я пытаюсь в общем случае преобразовать пользовательский ввод в простые или сложные типы:

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; }
}

Для преобразования в мой класс "Пользователь" я всегда получаю исключение "Неверное приведение из 'System.String'в 'ConsoleApplication1.User'. "Кто-нибудь знает, как это исправить?

Если я попробую что-то подобное (не общее), оно будет работать просто идеально:

Console.WriteLine((string) ((User) "Peggy Sue"));

Ответы [ 4 ]

6 голосов
/ 23 марта 2011

Нет, Convert.ChangeType работает только с фиксированным набором типов, я полагаю ... или, если исходный объект реализует IConvertible, он может вызвать IConvertible.ToType. Это означает, что вы могли бы реализовать IConvertible в своем классе User и иметь

Convert.ChangeType(user, typeof(string))

работает, но это не будет работать наоборот.

У вас есть фиксированный набор типов, которые нужно конвертировать? Если это так, у вас может быть Dictionary<Type, Func<string, object>>, который вы будете заполнять делегатами конверсии. Тогда вам просто нужно вызвать соответствующее преобразование и привести возвращаемое значение. Это некрасиво, но, вероятно, ваш лучший выбор.

5 голосов
/ 23 марта 2011

Один из вариантов здесь может заключаться в том, чтобы связать TypeConverter с интересующими вас типами (вы можете сделать это во время компиляции через [TypeConverter(...)], или есть хитрость для этого во время выполнения, если вы не контролируете типы).

Тогда это:

TypeConverter conv = TypeDescriptor.GetConverter(typeof(T));
T obj = (T)conv.ConvertFromString(text); // or ConvertFromInvariantString
2 голосов
/ 23 марта 2011

Я исправил это. Проверьте это:

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;
  }
}
0 голосов
/ 23 марта 2011

Если вы хотите преобразовать в числовые типы, я предпочитаю гораздо более краткие и содержательные:

decimal.Parse(someString)

Или, в вашем примере:

new User(userName)

Нет причин создавать целый метод (или класс, если в будущем вы решите «сделать это многоразовым»), чтобы просто обернуть приведение. Это особенно верно, когда язык уже имеет менее непрозрачный способ выразить намерения вашего кода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...