C# While循环到linq

C# While循环到linq,c#,linq,C#,Linq,有没有办法用linq来积累一些东西 初始代码: Something oneItem; List<Something> allItems; while ((oneLine = _generator.GenerateSomething()) != null) allItems.Add(_generator.CurrentItem); 实际上,如果generator内嵌IEnumerable,它会非常好,我会这样使用它: var allItems = _generator.Tak

有没有办法用linq来积累一些东西

初始代码:

Something oneItem;
List<Something> allItems;
while ((oneLine = _generator.GenerateSomething()) != null)
    allItems.Add(_generator.CurrentItem);
实际上,如果generator内嵌IEnumerable,它会非常好,我会这样使用它:

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

最后一个很容易理解,我想接近它,我可以使用一种包装器,在给定生产方法_generator.GenerateSomething和停止条件item==null的情况下生成机器状态。但出于某种原因,我无法编写这个附加类。

您可以创建如下迭代器方法:

static IEnumerable<string> ReadAllLines()
{
    string line;
    while ((line = Console.ReadLine()) != string.Empty)
        yield return line;
}
或在其上进行过滤:

ReadAllLines().Where(line => line.Contains("cool"));
试试这个:

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

编写一个与File.ReadLines在概念上等效的函数。将用于从控制台读取行的代码抽象掉一次,以便可以重用

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

是的,利用延迟执行是可能的,但我怀疑它是否比原始解决方案更具可读性:

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

它之所以有效,是因为LINQ语句不会立即执行,TakeWhile可以确保迭代及时停止。

还有另一种方法:

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

为什么要这样?初始代码优雅地表达了正在发生的事情。虽然Linq是新的黑色,但穿黑色并不总是合适的。在我看来,这个代码清晰简洁。很容易维护。@Servy:对不起,我不同意。循环习惯用法是软件工程中最基本的习惯用法之一。我认为Servy指的是循环条件中的副作用分配。太糟糕了,我们不能有静态扩展方法。Console.ReadLines会很好!为TextReader创建一个扩展方法,然后在Console.In上调用它。也许你应该在发布之前尝试一下。我正在利用Linq的工作原理,这一点都不粗糙!这不是在利用任何东西。这是对linq的滥用。@Jonesy:它不会创建一个包含20亿项的一次性对象。Enumerable.YieldTrueForever将像种子一样工作,如果它存在的话。它不会创建20亿个对象,你必须知道Linq是如何工作的才能理解它。是的,这就是我一直在寻找的。@EricJ。当你不需要看它的时候,它有多简单就无关紧要了。它在一个黑盒子后面。这就是黑匣子的优点,你可以随心所欲地编写它,而不需要专注于使隐藏的代码简短、简洁等@Servy:allLines是一个IEnumerable。你需要在最后有一个.ToList才能得到他的原始结果。@EricJ。如果他真的需要一份清单,他知道如何得到一份。他的编辑表明,事实并非如此。他是否想/是否需要添加它取决于他。我想也许我不完全理解收益率关键字,但是whiletrue的目的是什么?它能被移除吗?
var allLines = Generate(() => Console.ReadLine())
    .TakeWhile(line => line != null);
var lines = (from _ in Enumerable.Range(0, int.MaxValue)
             select Console.ReadLine())
            .TakeWhile(s => s != null)
            .ToList();
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(...)