LINQ以函数的结果作为源延迟执行(例如Console.ReadLine)

时间:2017-08-10 11:49:56

标签: c# linq deferred-execution

函数的结果是 LINQ查询的来源。我想让它在懒惰的情况下进行评估,每次我使用查询时,都不会在创建时锁定。这是我的意思的一个例子:

var query = from c in Console.ReadLine()
            group c by char.IsDigit(c) into gr
            select new { IsDigit = gr.Key, Count = gr.Count() };

Console.WriteLine()只运行一次 - 创建query时,即使没有像ToList()那样调用终止方法。我想要的是Console.WriteLine()(或其他任何功能)仅在我使用ToList()Count()等查询时执行。

3 个答案:

答案 0 :(得分:4)

如果你不介意一些额外的基础设施,那也不是太糟糕 - 你可以创建一个DeferredEnumerable<T>课程,只要每次被问到就执行给定的代理人对于迭代器。然后,静态非泛型类可以帮助进行类型推断。完整的例子:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

// Just for type inference...
public static class DeferredEnumerable
{
    public static IEnumerable<T> For<T>(Func<IEnumerable<T>> func) =>
        new DeferredEnumerable<T>(func);
}

public sealed class DeferredEnumerable<T> : IEnumerable<T>
{
    private readonly Func<IEnumerable<T>> func;

    public DeferredEnumerable(Func<IEnumerable<T>> func)
    {
        this.func = func;
    }

    public IEnumerator<T> GetEnumerator() => func().GetEnumerator();

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

class Test
{
    static void Main()
    {
        var query = 
            from c in DeferredEnumerable.For(Console.ReadLine)
            group c by char.IsDigit(c) into gr
            select new { IsDigit = gr.Key, Count = gr.Count() };


        Console.WriteLine("First go round");
        Console.WriteLine(string.Join(Environment.NewLine, query));

        Console.WriteLine("Second go round");
        Console.WriteLine(string.Join(Environment.NewLine, query));
    }
}

答案 1 :(得分:0)

我找到了2个解决方案,但它们真的很难看,我不想使用它们

解决方案1 ​​

这个特别难看,因为你需要一个额外的功能(它不能是匿名的)

static IEnumerable<string> GetDeferredConsoleReadLine()
{
    yield return Console.ReadLine();
}

var query = from line in GetDeferredConsoleReadLine()
            from c in line
            group c by char.IsDigit(c) into gr
            select new { IsDigit = gr.Key, Count = gr.Count() };

这使用延迟执行函数结果的可枚举函数{{1}。

解决方案2

这在另一个内部使用LINQ查询的另一个笨重的构造,它返回一个元素(事情是它需要一个源 - 我使用一个元素字符串并丢弃结果,但这不是很干净)< / p>

yield return

我有没有其他方法可以做到这一点,可能在查询中没有var query = from line in from _ in "1" select Console.ReadLine() from c in line group c by char.IsDigit(c) into gr select new { IsDigit = gr.Key, Count = gr.Count() };

答案 2 :(得分:0)

您可以将查询放在单独的方法中。

static void Main(string[] args)
{
    while (true)
    {
        foreach (var y in RunQuery()) {
            Console.WriteLine($"{y.IsDigit}: {y.Count}");
        }
    }
}

class A{public bool IsDigit { get; set; } public int Count { get; set; } }

private static IEnumerable<A> RunQuery()
{
    return from c in Console.ReadLine()
                group c by char.IsDigit(c) into gr
                select new A { IsDigit = gr.Key, Count = gr.Count() };
}