Comma separated string to generic list

2019-05-20 22:18发布

I was able to convert comma separated string to an IList<int> but how can I modify the same to get IList<T> where T will be passed as one of the input parameter?

i.e if I need IList<int> I will pass "int" as parameter, if I need IList<string> I will pass "string" as parameter.

My idea is to get the type whether it is int or string through input parameter and use reflection and convert the string to respective list

Code to convert comma separated string as IList<int>

public static IList<int> SplitStringUsing(this string source, string seperator =",")
{
     return source.Split(Convert.ToChar(seperator))
                  .Select(x => x.Trim())
                  .Where(x => !string.IsNullOrWhiteSpace(x))
                  .Select(int.Parse).ToList();
}

Note: Above code isn't tested yet

I am looking for something like

public static IList<T> SplitStringUsing(this string source, string seperator =",", T t)
{
find the type of t and convert it to respective List
}

3条回答
The star\"
2楼-- · 2019-05-20 22:48

I think you need Convert.ChangeType, like this. Its not fully tested, compile and fix.

public static IList<T> SplitStringUsing(string source, string seperator =",")
    {
         return source.Split(Convert.ToChar(seperator))
                      .Select(x => x.Trim())
                      .Where(x => !string.IsNullOrWhiteSpace(x))
                      .Select((T)Convert.ChangeType( x, typeof( T ) )).ToList();
    }
查看更多
太酷不给撩
3楼-- · 2019-05-20 22:50

You can use Convert.ChangeType(object,string) for parsing to the base types supported by the System.Convert class, or any other class that implements the IConvertible interface

public static IList<T> SplitStringUsing<T>(string source, string seperator = ",")
where T:IConvertible
{
        return source.Split(Convert.ToChar(seperator))
                     .Where(x => !string.IsNullOrWhiteSpace(x))
                     .Select(x=>Convert.ChangeType(x,typeof(T)))
                     .Cast<T>()
                     .ToList();
}

To avoid localization issues, you should probably add an IFormatProvider parameter as well, to allow the caller to specify the culture to use or default to the current culture, eg:

public static IList<T> SplitStringUsing<T>(string source, 
    string seperator = ",",
    IFormatProvider provider =null)
    where T:IConvertible
{
    return source.Split(Convert.ToChar(seperator))
                    .Where(x => !string.IsNullOrWhiteSpace(x))
                    .Select(x=>Convert.ChangeType(x,typeof(T),provider))
                    .Cast<T>().ToList();
}

For a more generic case, you can pass the parsing code as a lambda to the function:

    public static IList<T> SplitStringUsing<T>(string source, 
        Func<string,T> parser,  
        string seperator = ",")
    {
        return source.Split(Convert.ToChar(seperator))
            .Where(x => !string.IsNullOrWhiteSpace(x))
            .Select(parser)
            .ToList();
    }

and call it like this:

var l1 = SplitStringUsing(x,s=>double.Parse(s,NumberStyles.HexNumber,
                                              CultureInfo.InvariantCulture));

You can have both methods in your code and the compiler will pick the correct overload.

查看更多
孤傲高冷的网名
4楼-- · 2019-05-20 23:07

I would like to extend the answer of @PanagiotisKanavos.

Especially the generic approach with:

public static class StringToListExtension
{
    //see https://msdn.microsoft.com/en-us/library/System.String.Split.aspx
    //and https://msdn.microsoft.com/en-us/library/bb548891.aspx

    //this is the generic approach offering enough possibilies to use
    public static IEnumerable<TResult> MapStringValues<TResult>(this String source, Func<String, TResult> itemMapper, String[] separator, StringSplitOptions options)
    {
        if (null == source) throw new ArgumentNullException("source");
        return source.Split(separator, options).Select(itemMapper);
    }

    //add your implementation using MapStringValues<T>
    public static IList<Int32> MapToInt32ListUsingParse(this String source, String[] separator, StringSplitOptions options)
    {
        return MapStringValues<Int32>(source, Int32.Parse, separator, options).ToList();
    }

    //or more convenient
    public static IList<Int32> DefaultMapToIntList(this String source)
    {
        return MapStringValues<Int32>(source, Int32.Parse, DefaultSeparator, StringSplitOptions.RemoveEmptyEntries).ToList();
    }

    private static readonly String[] DefaultSeparator = new []{ "," };
}

You would use that code:

String values = "some,text,with,commas";
List<String> l1 = values.MapStringValues<String>(s => s, new []{ "," }, StringSplitOptions.None).ToList();

values = "2,4,,5,6";
IList<Int32> l2 = values.MapToInt32ListUsingParse(new []{ "," }, StringSplitOptions.RemoveEmptyEntries);

values = "2,4,,5,6";
IList<Int32> l3 = values.DefaultMapToIntList();

You can add convenience implementations for all your String to T cases. If you don't want exceptions to be thrown just implement a parse function using Int32.TryParse, etc.

查看更多
登录 后发表回答