C# LINQ将函数结果作为源延迟执行(例如Console.ReadLine)

C# LINQ将函数结果作为源延迟执行(例如Console.ReadLine),c#,linq,deferred-execution,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仅在创建查询时运行一次,即使没有

函数的结果是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仅在创建查询时运行一次,即使没有对其调用终止方法(如ToList)。我希望Console.WriteLine或任何其他函数仅在我使用ToList或Count等查询时执行。

我找到了两种解决方案,但它们非常难看,我不想使用它们

解决方案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() };

有没有其他方法可以做到这一点,而不必在查询中选择Many?

如果您不介意一些额外的基础设施,这也不算太糟糕-您可以创建一个可延迟重新计算的类,它在每次请求迭代器时只执行给定的委托。然后,静态非泛型类可以帮助进行类型推断。完整示例:

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

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

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

我也这么想,但只使用静态泛型迭代器方法。@Ivan:作为一种类型,它更易于重用。当然。一旦它被封装在一个公共静态方法中,实现就不重要了,可以是这样或那样。莫林的好人选@IvanStoev:没错,静态类可能是唯一公开的API。。。不确定。至于MoreLINQ,请随意提交一个github问题。这不是真正的可伸缩性,因为您必须为代码中要使用的每个查询创建一个新函数和一个类
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() };
}