我想将字符串转换为给定的泛型类型T
。它可以是基本类型或字符串(例如int
或string
),也可以是基本类型或字符串的数组(例如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子句中使用任何特定于数组的代码,因为它必须与标量兼容。当E[]
实际上是一个数组时,C#无法通过T
选择适当的重载。
我该怎么办?
答案 0 :(得分:6)
我会创建一些您注册的自定义解析器列表,然后才会使用,因为您似乎想要使用自定义规则:
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
现在,这是一个非常简单的实现。您可能希望扩展它,而不是使用类型Func<string, T>
,它可能使用IStringParser
接口,如果需要,您可以提供更深入的解析实现。此外,您可能希望将其设置为线程安全(除非您确定这不是问题,或者您确定在启动时注册之前任何用法)
编辑:如果你真的,真的,真的想要一个函数只是会计你的逗号分隔数组,那么你可以使用它:
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;
}
但这感觉错误。必须有一个更好的方法,并避免亚历山大的答案中的双重通用输入,但你去。
答案 1 :(得分:2)
另一种具有显式元素类型传递的解决方案:
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")
答案 2 :(得分:0)
也许这样的事情会有所帮助:
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();
}