如何从逗号分隔的字符串创建 List<T>?

17

假设有一个变量

string ids = Request.QueryString["ids"]; // "1,2,3,4,5";

有没有办法在不像下面这样做的情况下将其转换为List
List<int> myList = new List<int>();

foreach (string id in ids.Split(','))
{
    if (int.TryParse(id))
    {
        myList.Add(Convert.ToInt32(id));
    }
}

你能具体说明一下你想要的行为吗?就像上面那样,即忽略不是Int32的位吗?没有一个答案将其考虑在内。 - Ruben Bartelink
我正在默默地忽略错误的ID,因为它们是由内容编辑输入的,他们想将某些图像拉到他们的页面上,不过我可以在字段上进行验证,以防止这种情况发生。 - Nick Allen
是的,那正是我在暗示的。 - Ruben Bartelink
这个几乎重复的问题https://dev59.com/C3NA5IYBdhLWcg3wjOzf,比6小时后发布的那个问题更明确地要求一个数字字符串,因此它只有一个简单的答案。 - goodeye
更多通用转换请参考http://stackoverflow.com/questions/28233115/comma-separated-string-to-generic-list - Gopi
6个回答

50

要从零开始创建列表,请使用LINQ:

ids.Split(',').Select(i => int.Parse(i)).ToList();

如果您已经有了列表对象,请省略ToList()调用并使用AddRange:

myList.AddRange(ids.Split(',').Select(i => int.Parse(i)));

如果字符串中的某些条目可能不是整数,您可以使用TryParse:

int temp;
var myList = ids.Split(',')
    .Select(s => new { P = int.TryParse(s, out temp), I = temp })
    .Where(x => x.P)
    .Select(x => x.I)
    .ToList();

还有一种最终(较慢)的方法避免使用临时变量/TryParse但跳过无效条目,就是使用正则表达式:

var myList = Regex.Matches(ids, "[0-9]+").Cast<Match>().SelectMany(m => m.Groups.Cast<Group>()).Select(g => int.Parse(g.Value));
然而,如果你的输入之一超出了int类型的范围(999999999999),这可能会导致错误。

我已经编辑了我的答案,以反映内联TryParse实现所需的内容。这就是Convert.ToNullableInt32实现有用的地方 :) - Jason
你确定它可以编译通过吗?(在LINQPad中:D) - Ruben Bartelink
是的。唯一让我不太确定的是属性初始化器中操作的顺序('out temp' 接着 I = temp)。我相信这是有保证按顺序执行的。 - Jason
同意从执行顺序的角度来看,它应该能够工作,但我讨厌像这样由下面的东西共享的临时变量。扩展/帮助方法是解决这些混乱的方法。 - Ruben Bartelink
从stackoverflow.com上一个几乎重复的问答的评论中(https://dev59.com/C3NA5IYBdhLWcg3wjOzf#911729),`Select(i => int.Parse(i))部分可以简化为Select(int.Parse)`。 - goodeye

8
这应该可以解决问题:
myList.Split(',').Select(s => Convert.ToInt32(s)).ToList();

如果列表可能包含除整数以外的其他数据,则应包括TryParse调用。请参见已接受的答案。

好吧,同样的评论,不要使用TryParse :P(但撤销-1!) - Ruben Bartelink
是的,TryParse没有被包含进来,而且由于数据是查询字符串,它应该在那里。但是感谢您撤销了-1 :) - Ronald Wildenberg

4

使用Linq:

myList.AddRange(ids.Split(',').Select(s => int.Parse(s));

或者直接:
var myList = ids.Split(',').Select(s => int.Parse(s));

此外,为了防止编译器显式生成(大部分是冗余的)lambda,请考虑以下方法:
var myList = ids.Split(',').Select((Func<string, int>)int.Parse);

提示: 微优化。

还有一个 TryParse,应该代替 Parse(仅当可能存在无效输入并且应该被静默处理时)使用。然而,其他人已经发布了使用 TryParse 的解决方案,所以我肯定不会再提供。只要记住不要重复计算。


2
这样你就可以得到一个布尔值列表。Int32.TryParse返回一个布尔值。 - Ronald Wildenberg
1
@Konrad,我已经帮你修复了。希望你不介意 ;) - LukeH
我会接受我的网站上的编译错误😜我相信Konrad会像Jason和我的回答一样返回正确的结果!这件事情已经失控了! - Ruben Bartelink
说真的,伙计们。首先,感谢Luke编辑我的问题。你为什么要还原?编辑是非常准确的——这是我自己愚蠢的错误。当然,TryParse有好处,但这是完全不同的讨论。特别是@Ruben:请解释一下你对TryParse的+1…… - Konrad Rudolph
@Konrad:有点晚了,但是在你发帖的时候,已经有很多答案提供了比你更简单的答案。问题在于,他们忽略了提问者代码的一个关键部分——他正在使用TryParse来有条件地跳过错误数据。(请参见问题中的评论“I am silently ignoring bad ids because they are entered by content editors who want to pull in certain images on to their pages, however I can put validation on the field to stop that from happening I suppose”)。编辑使你的答案与其他答案语义不同。 - Ruben Bartelink
显示剩余4条评论

3

或者像你的例子中一样使用TryParse

var res = ids.Split(',').Where(x => { int tmp; return int.TryParse(x, out tmp); }).Select(x => int.Parse(x)).ToList();

3
为了在性能特征和行为方面与请求相匹配,它应该做同样的事情,而不是去做正则表达式或者不做“TryParse”:-
ds.Split(',')
  .Select( i => {
    int value; 
    bool valid = int.TryParse(out value); 
    return new {valid, value}
  })
  .Where(r=>r.valid)
  .Select(r=>r.value)
  .ToList();

但是,虽然正确,看起来相当丑:D

借鉴Jason评论中的提示:-

ds.Split(',')
  .Select( i => {
    int value; 
    bool valid = int.TryParse(out value); 
    return valid ? new int?( value) : null;
  })
  .Where(r=>r != null)
  .Select(r=>r.Value)
  .ToList();

或者

static class Convert
{
  public static Int32? ConvertNullable(this string s)
  {
    int value; 
    bool valid = int.TryParse(out value); 
    return valid ? new int?( value) : null;
  }
}

ds.Split(',')
  .Select( s => Convert.ConvertNullable(s))
  .Where(r=>r != null)
  .Select(r=>r.Value)
  .ToList();

使用内联声明,Select 部分可以稍微简单一些: .Select(s => int.TryParse(s, out int d)? (int?)d : null) - montonero

2

目前面临的问题是如何处理不是整数的值(假设我们会得到一些不是整数的值)。一个想法可能是简单地使用正则表达式:

^-?[0-9]+$

现在,我们可以将所有这些结合起来(如Konrad的示例所示):

var myList = ids.Split(',').Where(s => Regex.IsMatch(s, "^-?[0-9]$")).Select(s => Convert.ToInt32(s)).ToList();

这应该可以完成任务。


正则表达式并没有表示[1-9]+,但即使这样,它仍然可能超出范围并抛出异常 - 这与tryparse不同。 - Ruben Bartelink
关于超出范围的问题 - 非常正确。然而,我不明白为什么您想将正则表达式更改为[1-9]+,这会使数字“10”成为无效数字。此外,我将稍微修改正则表达式以考虑负数。 - kastermester
抱歉,我的意思是[0-9],(我真正想表达的是\d)。我只是在指出代码更复杂、不同于正则表达式的两个问题,并且你的正则表达式仍然没有在[0-9]后面加上"+",所以只能使用1位数字,因此永远无法超出范围:P - Ruben Bartelink

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接