【问题标题】:Convert System.String generically to any complex type using "Convert.ChangeType()"使用“Convert.ChangeType()”将 System.String 一般转换为任何复杂类型
【发布时间】:2011-07-21 02:04:08
【问题描述】:

我尝试一般地将用户输入转换为简单或复杂类型:

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"));

【问题讨论】:

  • User对象是否提供了默认构造函数??

标签: c# string type-conversion


【解决方案1】:

不,Convert.ChangeType 只适用于一组固定的类型,我相信......或者如果原始对象实现了IConvertible,它可以调用IConvertible.ToType。这意味着您可以在您的 User 类中实现 IConvertible 并拥有

Convert.ChangeType(user, typeof(string))

工作,但 不会反过来工作。

您是否有一组需要转换的固定类型?如果是这样,您可以拥有一个Dictionary&lt;Type, Func&lt;string, object&gt;&gt;,您将在其中填充转换代表。然后你只需要调用适当的转换并转换返回值。这很丑,但可能是你最好的选择。

【讨论】:

    【解决方案2】:

    如果您希望转换为数字类型,我更喜欢更简洁和意图暴露的:

    decimal.Parse(someString)
    

    或者,在您的示例中:

    new User(userName)
    

    没有理由创建一个完整的方法(或类,如果您将来决定“使其可重用”)只是为了包装一个演员表。当语言已经有一种不太透明的方式来表达代码的意图时,尤其如此。

    【讨论】:

    • 是的,您是对的,但我发布的示例只是实际 GetUserInput 方法的简单示例。例如: static T GetUserInput() { var input = Console.ReadLine(); if (string.IsNullOrEmpty(input) || input.Trim() == string.Empty) return GetUserInput();返回 (T)Convert.ChangeType(输入, typeof(T)); }
    • @Maik:您可能应该拆分这段代码,因为它做了两件不同的事情(接受用户输入和解析用户输入)。尽管代码很短,而且 .Net 似乎支持这种情况,但在你的设计中结合这种类型的东西通常会让你在更大的项目中受到影响。见en.wikipedia.org/wiki/Single_responsibility_principle
    【解决方案3】:

    这里的一个选项可能是将 TypeConverter 与您关心的类型相关联(您可以在编译时通过 [TypeConverter(...)] 执行此操作,或者如果您不控制类型,则可以在运行时执行此操作)。

    那么就是:

    TypeConverter conv = TypeDescriptor.GetConverter(typeof(T));
    T obj = (T)conv.ConvertFromString(text); // or ConvertFromInvariantString
    

    【讨论】:

    • 听起来不错!如何将我的类“User”作为“TypeConverter”对象引入?我收到一个“NotSupportedException”,说“TypeConverter 无法从 System.String 转换。”!
    【解决方案4】:

    我修好了。检查这个:

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

    【讨论】:

    • 感谢分享!这是一个很好的解决方案。我已经在努力使用反射来查找与目标类型匹配的隐式运算符,但这要优雅得多!
    猜你喜欢
    • 2019-12-07
    • 2013-12-21
    • 2012-09-07
    • 1970-01-01
    • 1970-01-01
    • 2014-09-14
    • 1970-01-01
    • 1970-01-01
    • 2014-03-14
    相关资源
    最近更新 更多