2012-06-02 32 views
1

我想创建一个拆分扩展,它允许我将任何字符串拆分为强类型列表。我有一个良好的开端,但由于我将在许多项目中重复使用它,我想从社区获得输入(因此可以将它添加到自己的工具箱中)。分割字符串扩展与泛型类型?

public static class Converters 
{ 
    public static IEnumerable<T> Split<T>(this string source, char delimiter) 
    { 
     var type = typeof(T); 

     //SPLIT TO INTEGER LIST 
     if (type == typeof(int)) 
     { 
      return source.Split(delimiter).Select(n => int.Parse(n)) as IEnumerable<T>; 
     } 

     //SPLIT TO FLOAT LIST 
     if (type == typeof(float)) 
     { 
      return source.Split(delimiter).Select(n => float.Parse(n)) as IEnumerable<T>; 
     } 

     //SPLIT TO DOUBLE LIST 
     if (type == typeof(double)) 
     { 
      return source.Split(delimiter).Select(n => double.Parse(n)) as IEnumerable<T>; 
     } 

     //SPLIT TO DECIMAL LIST 
     if (type == typeof(decimal)) 
     { 
      return source.Split(delimiter).Select(n => decimal.Parse(n)) as IEnumerable<T>; 
     } 

     //SPLIT TO DATE LIST 
     if (type == typeof(DateTime)) 
     { 
      return source.Split(delimiter).Select(n => DateTime.Parse(n)) as IEnumerable<T>; 
     } 

     //USE DEFAULT SPLIT IF NO SPECIAL CASE DEFINED 
     return source.Split(delimiter) as IEnumerable<T>; 
    } 
} 
+0

您应该/可以使用else if而不是多个单个if语句。只有一个会匹配。 – mfussenegger

+1

@mfussenegger:由于每个'if'块都包含'return'语句,所以'else'变得多余。 – Douglas

+1

IMO,如果您的泛型方法依赖于测试泛型参数的类型,那么它可能不是泛型的好候选。 – spender

回答

5

虽然我与李的建议同意,我个人不认为这是值得定义一种新的扩展方法,可以通过标准的LINQ操作简单地实现:

IEnumerable<int> ints = "1,2,3".Split(',').Select(int.Parse); 
+1

我必须同意。考虑到这个答案的简洁性,将其转为扩展方法似乎只是增加了噪音。 – spender

9

我想补充一个参数转换功能:

public static IEnumerable<T> Split<T>(this string source, Func<string, T> converter, params char[] delimiters) 
{ 
    return source.Split(delimiters).Select(converter); 
} 

而且你可以把它作为

IEnumerable<int> ints = "1,2,3".Split<int>(int.Parse, ','); 

我也会考虑将其重命名,以避免与String.Split混乱实例方法,因为这会使重载分辨率变得复杂,并且行为与其他方法不同。

编辑:如果你不想指定转换功能,你可以使用的类型转换器:

public static IEnumerable<T> SplitConvert<T>(this string str, params char[] delimiters) 
{ 
    var converter = TypeDescriptor.GetConverter(typeof(T)); 
    if (converter.CanConvertFrom(typeof(string))) 
    { 
     return str.Split(delimiters).Select(s => (T)converter.ConvertFromString(s)); 
    } 
    else throw new InvalidOperationException("Cannot convert type"); 
} 

这使得转换扩展到其他类型的,而不是依靠一个预先定义的列表。

+0

谢谢,但有点击败泛型类型的目的和易于重用,如果我不得不通过函数。 – Basem

2
public static IEnumerable<T> Split<T> 
     (this string source, char delimiter,Converter<string,T> func) 
{ 
    return source.Split(delimiter).Select(n => func(n))); 
} 

例子:

"...".Split<int>(',',p=>int.Parse(p)) 

或者你可以使用Converter.ChangeType没有定义功能:

public static IEnumerable<T> Split<T>(this string source, char delimiter) 
{ 
    return source.Split(delimiter).Select(n => (T)Convert.ChangeType(n, typeof(T))); 
} 
+1

+1,但请注意,调用可能更简洁:'“...”。Split(',',int.Parse)'而不是'“...”分割(',',p = > int.Parse(p))' – phoog

+0

@phoog,我认为p => int.Parse(p)比int.Parse更灵活,因为您可以使用int.Parse()的任何重载,例如p => int。解析(p ,, System.Globalization.NumberStyles.AllowDecimalPoint)。 –

1

我不喜欢这种方法。当你处理比int更复杂的数据类型时,从字符串解析数据类型(某种反序列化)是一种非常类型和内容敏感的过程。例如,DateTime.Parse会分析日期using the current culture,因此您的方法不会为跨系统的日期提供一致或可靠的输出。它还试图以不惜代价解析日期,因此它可能跳过了在某些情况下可能被认为是错误输入的内容。

任何字符串转换成一个强类型列表不能真正与使用硬编码的转换,特别是如果你的目标是广泛的可用性一个方法来完成分裂的目的。即使您通过新的转换重复更新。最好的办法就是像上面提到的Douglas "1,2,3".Split(",").Select(x => whatever)一样。它也很清楚正在发生什么样的转换。