2012-06-02 6 views
1

Я хотел бы создать расширение Split, которое позволит мне разбить любую строку на строго типизированный список. У меня есть начало, но поскольку я собирался повторно использовать это во многих проектах, я хотел бы получить информацию от сообщества (и поэтому вы можете добавить его в свой собственный набор инструментов;) Любые идеи отсюда?Расширение строки с общим типом?

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. Только один будет соответствовать. – 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 потому что это усложняет разрешение перегрузки и ведет себя по-другому по отношению к другим.

EDIT: Если вы не хотите, чтобы указать функцию преобразования, вы можете использовать преобразователи типа:

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, но обратите внимание, что вызов может быть более кратким: '" ... ". Разделить (',', int.Parse)' вместо '" ... ". Split (',', p = > int.Parse (p)) ' – phoog

+0

@phoog, я думаю, p => int.Parse (p) более гибкий, чем int.Parse, потому что вы можете использовать любую перегрузку int.Parse(), такую ​​как p => int. Анализировать (р ,, System.Globalization.NumberStyles.AllowDecimalPoint). –

1

Мне не нравится этот метод. Анализ типов данных из строк (своего рода десериализация) - это очень чувствительный к типу и контент процесс, когда вы имеете дело с более сложными типами данных, чем int. Например, DateTime.Parse анализирует дату using the current culture, поэтому ваш метод не будет предоставлять последовательный или надежный вывод для дат в разных системах. Он также пытается проанализировать дату любой ценой, чтобы он мог пропустить то, что может считаться плохим входом в некоторых ситуациях.

Целью разделить любую строку на строго типизированный список не может быть осуществлен с помощью одного метода, который использует преобразованные в твердом переплете преобразования, особенно если ваша цель - широкое использование. Даже если вы обновляете его повторно с помощью новых преобразований. Лучший способ сделать это - только "1,2,3".Split(",").Select(x => whatever), как и Дуглас, предложенный выше. Также очень ясно, что такое конверсия.

Смежные вопросы