【问题标题】:Comma separated string to generic list逗号分隔的字符串到通用列表
【发布时间】:2015-03-29 18:21:34
【问题描述】:

我能够将逗号分隔的字符串转换为 IList<int>,但我如何修改它以获得 IList<T> 其中 T 将作为输入参数之一传递?

即如果我需要IList<int>,我将传递“int”作为参数,如果我需要IList<string>,我将传递“string”作为参数。

我的想法是通过输入参数获取类型是int还是string,并使用反射并将字符串转换为相应的列表

将逗号分隔的字符串转换为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();
}

注意:以上代码尚未测试

我正在寻找类似的东西

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

【问题讨论】:

  • 如果使用 IsNullOrWhitespace,则无需修剪。
  • 不提供参数T t,而是提供一个“选择器”函数Func&lt;string, T&gt;,然后用它代替int.Parse,怎么样?所以你可以像IList&lt;int&gt; list = "1, 2, 3, 4".SplitStringUsing(",", int.Parse); 一样使用它。如果您需要例如双打,您可以将其更改为IList&lt;double&gt; list = "1, 2, 3, 4".SplitStringUsing(",", double.Parse);

标签: c# string reflection collections


【解决方案1】:

我想扩展@PanagiotisKanavos 的answer

尤其是通用方法:

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 []{ "," };
}

您将使用该代码:

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();

您可以为所有 String to T 案例添加便利实现。如果您不想抛出异常,只需使用 Int32.TryParse 等实现解析函数即可。

【讨论】:

    【解决方案2】:

    您可以使用Convert.ChangeType(object,string) 解析为System.Convert 类或任何其他实现IConvertible 接口的类所支持的基本类型

    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();
    }
    

    为避免本地化问题,您可能还应该添加一个 IFormatProvider 参数,以允许调用者指定要使用的文化或默认为当前文化,例如:

    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();
    }
    

    对于更通用的情况,您可以将解析代码作为 lambda 传递给函数:

        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();
        }
    

    然后这样称呼它:

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

    您可以在代码中同时使用这两种方法,编译器会选择正确的重载。

    【讨论】:

    • 如果你事先知道数据的类型,这是一个很好的解决方案
    • 这就是泛型在 C# 上下文中的含义。该函数不知道类型并希望调用者指定它
    • 对..问题令人困惑我以为他想根据输入动态确定类型
    • 这对于一个小函数来说是不可能的 - 1 是 int、long、float、double 还是 decimal?一个字符甚至一个字符串?这需要确定解析规则和回退,知道调用的上下文(例如,列表是否会与其他字符串或数字组合)?
    • @PanagiotisKanavos no 我没有找到基于值(1 或 A)的类型,但我知道在开发过程中我是否需要整数列表、字符串列表或双列表
    【解决方案3】:

    我认为您需要像这样的 Convert.ChangeType。它没有完全测试,编译和修复。

    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();
        }
    

    【讨论】:

    • 这不会编译,因为ChangeType 返回object。您还需要使用 Cast&lt;T&gt;() 将结果转换为正确的类型
    • @PanagiotisKanavos 他正在 Select 中进行演员阵容。顺便说一句,它需要一点修改:.Select(x =&gt; (T)Convert.ChangeType( x, typeof( T ) )
    • @PanagiotisKanavos (T) 担任演员。但我不确定它是否有效。这就是我提到编译和修复的原因。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-10
    • 1970-01-01
    相关资源
    最近更新 更多