Если у меня есть, например, следующая строка:
"123; 3344; 4334; 12 дюймов
и я хочу эти числа в дженерике List<int>
, Я предполагаю, что не знаю о хорошем пути здесь кроме разделить в цикле и сделать, преобразование затем добавляет к a List<int>
посредством каждого повторения. У кого-либо есть другие способы пойти об этом?
Обновленный. Вот то, что я придумал. Я хочу сделать это старомодный путь, не с LINQ, потому что я пытаюсь поправиться только с строками, массивами, списками и управлением и преобразованием в целом.
public List<int> StringToList(string stringToSplit, char splitDelimiter)
{
List<int> list = new List<int>();
if (string.IsNullOrEmpty(stringToSplit))
return list;
string[] values = stringToSplit.Split(splitDelimiter);
if (values.Length <= 1)
return list;
foreach (string s in values)
{
int i;
if (Int32.TryParse(s, out i))
list.Add(i);
}
return list;
}
Это - новый строковый служебный метод, который я планирую на использовании каждый раз, когда я должен преобразовать разграниченный список строк для Списка
Таким образом, я возвращаю пустой список назад вызывающей стороне, если что-то перестало работать. Хороший/Плохой? действительно ли довольно распространено сделать это?
Да, существуют более "изящные" способы сделать это с LINQ, но я хочу сделать это вручную.. старый путь на данный момент только к моему собственному пониманию.
Кроме того, что беспокоит меня об этом:
list.AddRange(str.Split(';').Select(Int32.Parse));
это, я понятия не имею:
str.Split(';').Select(Int32.Parse)
просто сбои по любой причине... затем метод, в котором находится этот AddRange, собирается аварийно завершиться и если я не добавляю попытку/выгоду вокруг этой целой вещи, я завинчен, если я не обрабатываю его правильно.string str = "123;3344;4334;12";
List<int> list = new List<int>();
foreach (string s in str.Split(';'))
{
list.Add( Int32.Parse(s));
}
List<int> list = (from numString in "123;3344;4334;12".Split(';')
select int.Parse(numString)).ToList();
static int? ToInt32OrNull(string s)
{
int value;
return (Int32.TryParse(s, out value)) ? value : default(int?);
}
// ...
var str = "123;3344;4334;12";
var list = new List<int>();
list.AddRange(str.Split(';')
.Select(ToInt32OrNull)
.Where(i => i != null)
.Cast<int>());
Замечания автора вопроса:
Я не знаю хорошего способа, кроме как разделить в цикле и выполнить преобразование, а затем добавить в список
В общем, это основная причина, по которой LINQ был привнесен в C# - чтобы устранить необходимость работать с последовательностями значений, реализуя циклы, и вместо этого просто объявить о своем намерении преобразовать последовательность. Если вы когда-нибудь подумаете: "Я не знаю, как это сделать, кроме как с помощью цикла" - самое время обратить внимание на конструкцию LINQ, которая сделает всю работу за вас.
Обновление о производительности:
Ниже был задан вопрос о производительности LINQ. Хотя в комментариях отстаивается идея о том, что LINQ медленнее, поскольку мы получаем преимущества читаемости, сопровождаемости и композиции, есть еще один аспект, который дает LINQ легкое преимущество в производительности: параллелизм. Вот пример, где добавление всего одного вызова метода расширения, AsParallel()
, удваивает производительность. Это отличный пример того, как масштабирование побеждает микрооптимизацию, даже не требуя тщательных измерений. Заметьте, я не утверждаю, что микрооптимизация никогда не нужна, но с инструментами, доступными нам на этом уровне абстракции, необходимость в ней становится исчезающе малой.
class Program
{
private const int ElementCount = 10000000;
static void Main(string[] args)
{
var str = generateString();
var stopwatch = new Stopwatch();
var list1 = new List<int>(ElementCount);
var list2 = new List<int>(ElementCount);
var split = str.Split(';');
stopwatch.Start();
list1.AddRange(split
.Select(ToInt32OrNull)
.Where(i => i != null)
.Cast<int>());
stopwatch.Stop();
TimeSpan nonParallel = stopwatch.Elapsed;
stopwatch.Restart();
list2.AddRange(split
.AsParallel()
.Select(ToInt32OrNull)
.Where(i => i != null)
.Cast<int>());
stopwatch.Stop();
TimeSpan parallel = stopwatch.Elapsed;
Debug.WriteLine("Non-parallel: {0}", nonParallel);
Debug.WriteLine("Parallel: {0}", parallel);
}
private static String generateString()
{
var builder = new StringBuilder(1048576);
var rnd = new Random();
for (int i = 0; i < ElementCount; i++)
{
builder.Append(rnd.Next(99999));
builder.Append(';');
}
builder.Length--;
return builder.ToString();
}
static int? ToInt32OrNull(string s)
{
int value;
return (Int32.TryParse(s, out value)) ? value : default(int?);
}
}
Непараллельно: 00:00:07.0719911
Параллельно: 00:00:04.5933906
string myString = "123;3344;4334;12";
var ints = new List<int>();
(from s in myString.Split(';')
select int.Parse()).ToList().ForEach(i=>ints.Add(i));
Я слышал, что в .Net 4.0 к ForEach
добавили Enumerable
, так что ToList
там может быть не нужен (не могу проверить).