在c#中将字符串转换为泛型类型(基本类型或数组类型)

本文关键字:类型 数组 泛型类型 字符串 转换 | 更新日期: 2023-09-27 17:51:24

我想将字符串转换为给定的泛型T。它可以是基本类型或字符串(例如intstring),也可以是基本类型或字符串的数组(例如int[]string[])。我有以下功能:

T Str2Val<T>(string str)
{
  return (T)Convert.ChangeType(str, typeof(T));
}

它适用于基本类型。但是T是一个数组,E[]str是一个逗号分隔的值列表。

我可以很容易地检查T是否是一个数组与typeof(T).IsArray。那么我有两个解决方案:在同一个函数中使用标量解析数组,如下所示:

  if (!typeof(T).IsArray)
  {
    return (T)Convert.ChangeType(str, typeof(T));
  }
  else
  {
    // Handle an array
  }

或实现两个重载函数:一个用于泛型T,另一个用于泛型E[]。然而,这两种解决方案都失败了。我不能在else-子句中使用任何特定于数组的代码,因为它必须与标量兼容。当T实际上是一个数组时,c#不能为E[]选择适当的重载。

我该怎么办?

在c#中将字符串转换为泛型类型(基本类型或数组类型)

我会创建一些自定义解析器列表,您将注册然后稍后利用,因为您似乎想要使用自定义规则:

public static class StringParsers
{
    private static Dictionary<Type, object> Parsers = new Dictionary<Type, object>();
    public static void RegisterParser<T>(Func<string, T> parseFunction)
    {
        Parsers[typeof(T)] = parseFunction;
    }
    public static T Parse<T>(string input)
    {
        object untypedParser;
        if (!Parsers.TryGetValue(typeof(T), out untypedParser))
            throw new Exception("Could not find a parser for type " + typeof(T).FullName);
        Func<string, T> parser = (Func<string, T>)untypedParser;
        return parser(input);
    }
}

在应用程序初始化期间,您将注册您打算稍后在应用程序中使用的类型(我猜这是已知的,因为您正在使用泛型):

StringParsers.RegisterParser<string[]>(input => input.Split(','));
StringParsers.RegisterParser<int[]>(input => input.Split(',').Select(i => Int32.Parse(i)).ToArray());
StringParsers.RegisterParser<int>(input => Int32.Parse(input));

最后,您可以简单地调用它:

string testArrayInput = "1,2,8";
int[] integers = StringParsers.Parse<int[]>(testArrayInput); // {1, 2, 8}
string[] strings = StringParsers.Parse<string[]>(testArrayInput); // {"1", "2", "8"}
int singleInt = StringParsers.Parse<int>("9999"); //9999

现在,这是一个非常简单的实现。您可能希望扩展它,这样它就可以使用IStringParser接口而不是使用类型Func<string, T>,并且您可以在必要时提供更深入的解析实现。此外,您可能希望使其线程安全(除非您确定这不会是一个问题,或者如果您确定启动时的注册在任何用法之前是)

编辑:如果你真的,真的,真的想要在一个函数只是占你的逗号分隔数组,那么你可以使用这个:

public static T Str2Val<T>(string str)
{
    if (!typeof(T).IsArray)
        return (T)Convert.ChangeType(str, typeof(T));
    Type elementType = typeof(T).GetElementType();
    string[] entries = str.Split(',');
    int numberOfEntries = entries.Length;
    System.Array array = Array.CreateInstance(elementType, numberOfEntries);
    for(int i = 0; i < numberOfEntries; i++)
        array.SetValue(Convert.ChangeType(entries[i], elementType), i);
    return (T)(object)array;
}

但这感觉很不对。一定有更好的方法来避免Alexander的答案中的双重泛型输入,但这就是你的答案。

另一个显式元素类型传递的解决方案:

static T Str2Val<T>(string str)
{
    return (T)Convert.ChangeType(str, typeof(T));
}
static E[] Str2Val<T, E>(string str)
    where T : ICollection<E>
{
    return str.Split(',').Select(x => (E)Convert.ChangeType(x, typeof(E))).ToArray();
}
使用

Str2Val<int>("1")
Str2Val<int[], int>("1,3")

也许这样做会有所帮助:

dynamic Str2Val<T>(string str)
{
    if (!typeof(T).IsArray)
        return (T)Convert.ChangeType(str, typeof(T));
    var elementType = typeof(T).GetElementType();
    return str.Split(',').Select(x => Convert.ChangeType(x, elementType)).ToArray();
}