2014-08-29 5 views
2

Есть ли способ накопить что-то с помощью linq.While loop to linq

Начальный код:

Something oneItem; 
List<Something> allItems; 
while ((oneLine = _generator.GenerateSomething()) != null) 
    allItems.Add(_generator.CurrentItem); 

Я хотел бы что-то вроде:

var allItems = Enumerable.Take(()=>_generator.GenerateSomething()).While(item=>item !=null).ToList(); 

На самом деле, это было бы очень хорошо, если генератор impleted IEnumerable, я бы использовать его таким образом:

var allItems = _generator.TakeWhile(item !=null); 

Этот последний очень легко понять, я хотел бы его одобрить (я могу e - своего рода оболочка, которая генерирует состояние машины с учетом производственного метода (_generator.GenerateSomething()) и условие остановки (item == null). Но я не могу написать этот дополнительный класс или по какой-то причине).

+7

почему вы хотите этого? –

+0

Я нашел исходный код довольно грязным, мне хотелось бы что-то лучше написано – Toto

+11

Исходный код изящно выражает происходящее. В то время как Linq является новым Black, не всегда удобно носить черный цвет. –

ответ

6

Написать функцию, эквивалентную File.ReadLines в концепции. Выделите код для чтения строк с консоли один раз, чтобы его можно было повторно использовать.

public static IEnumerable<string> ReadLinesFromConsole() 
{ 
    while (true) 
    { 
     var next = Console.ReadLine(); 
     if (next == null) 
      yield break; 
     yield return next; 
    } 
} 

Тем не менее, если вы действительно хочу обобщать, вы можете. Здесь у вас есть простой генератор, принимающий функцию.

public static IEnumerable<T> Generate<T>(Func<T> generator) 
{ 
    while (true) 
     yield return generator(); 
} 

Это позволяет писать код, который вы имели в вашем примере:

var allLines = Generate(() => Console.ReadLine()) 
    .TakeWhile(line => line != null); 
+0

Да, это то, что я искал. – Toto

+2

Ничего себе, это проще, чем исходный код? –

+0

@EricJ. Когда вам никогда не нужно это видеть, это не имеет значения, насколько это просто. Это за черным ящиком. Это преимущество черного ящика, вы можете написать его, как хотите, и не нужно сосредоточиться на том, чтобы сделать скрытый код коротким, кратким и т. Д. – Servy

2

Вы можете создать метод итератора так:

static IEnumerable<string> ReadAllLines() 
{ 
    string line; 
    while ((line = Console.ReadLine()) != string.Empty) 
     yield return line; 
} 

И таким образом, вы можете передавать его или вызвать ToList() на нем:

ReadAllLines().ToList(); 

или фильтр на нем:

ReadAllLines().Where(line => line.Contains("cool")); 
+0

Слишком плохо, мы не можем иметь статические методы расширения. Console.ReadLines было бы неплохо! – Derek

+1

Сделайте метод расширения для 'TextReader', затем вызовите его на' Console.In'. –

+0

Было бы здорово, если бы не было ошибок компиляции ... –

2

Попробуйте следующее:

var lines = Enumerable.Range(0, int.MaxValue).Select(x => Console.ReadLine()).TakeWhile(x => !string.IsNullOrEmpty(x)).ToList(); 
+3

Возможно, вам стоит попробовать его перед публикацией. – brz

+0

Я использую то, как работает Linq, в этом нет ничего глупого! – brz

+1

это ничего не использует. это неправильное использование linq. –

0

Да, это возможно за счет использования отложенного выполнения, но я сомневаюсь, что это более читаемым, чем исходное решение:

var lines = (from _ in Enumerable.Range(0, int.MaxValue) 
      select Console.ReadLine()) 
      .TakeWhile(s => s != null) 
      .ToList(); 

(Это работает, потому что заявления LINQ не выполняются немедленно и TakeWhile гарантирует, что итерация останавливается время.)

0

И просто есть еще один подход:

public static class FuncExtensions 
{ 
    public static IEnumerabley<T> AsGenerator(this Func<T> func) 
    { 
     while(true) yield return func(); 
    } 
} 

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

Func<string> f = Console.ReadLine; 

f.AsGenerator().Select(...).ToList(); 

// this won't work unfortunately 
Console.ReadLine.AsGenerator().Select(...)