Как создать список <T> из строки, разделенной запятой?

Учитывая переменную

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

Есть ли способ преобразовать его в список, не делая что-то вроде

List<int> myList = new List<int>();

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

Ответ 1

Чтобы создать список с нуля, используйте 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();

Один последний (более медленный) метод, который позволяет избежать Temps/TryParse, но пропускает недопустимые записи, заключается в использовании Regex:

var myList = Regex.Matches(ids, "[0-9]+").Cast<Match>().SelectMany(m => m.Groups.Cast<Group>()).Select(g => int.Parse(g.Value));

Однако это может вызвать, если одна из ваших записей переполняет int (999999999999).

Ответ 2

Это должно сделать трюк:

myList.Split(',').Select(s => Convert.ToInt32(s)).ToList();

Если список может содержать другие данные, кроме целых чисел, следует включить вызов TryParse. См. Принятый ответ.

Ответ 3

Использование Linq:

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

Или напрямую:

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

Кроме того, чтобы предотвратить компилятор от явной генерации (в значительной степени избыточной) лямбда, рассмотрим:

var myList = ids.Split(',').Select((Func<string, int>)int.Parse);

(Подсказка: микро-оптимизация.)

Там также TryParse, который следует использовать вместо Parse (только), если возможен недопустимый ввод, и его следует обрабатывать молча. Однако другие разместили решения с использованием TryParse, поэтому я, конечно, не буду. Просто имейте в виду, что вы не должны дублировать расчет.

Ответ 4

Или включая TryParse как в вашем примере:

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

Ответ 5

Чтобы соответствовать запросу с точки зрения характеристик производительности и поведения, он должен делать то же самое и не уходить с doign regexes или не выполнять "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

Заимствование из подсказки в комментарии Джейсона: -

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

Ответ 6

Одна проблема заключается в том, как мы будем иметь дело со значениями, которые не являются целыми (предположим, что мы получим некоторые, которые не являются целыми). Одной из идей может быть просто использование регулярного выражения:

^ -? [0-9] + $

Теперь мы могли бы объединить все это с (как показано на примере Konrad):

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

Это должно выполнить эту работу.