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

问题描述:

函数的结果是LINQ查询的源代码。我希望它每次使用查询时都会进行懒惰评估,而不是在创建时锁定。这是我的意思的例子:LINQ延迟执行,函数结果作为源(例如Console.ReadLine)

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

如果您不介意多一点额外的基础架构,那也不算太糟糕 - 您可以创建一个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)); 
    } 
} 
+0

我在想同样的事情,但只使用静态泛型迭代器方法。 –

+0

@Ivan:作为一种类型,它更容易重用。 –

+0

当然。只要它被封装在公共静态方法中,实现并不重要(可能是一个或另一个)。 MoreLinq的好候选人? :) –

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

这使用延迟执行函数结果的枚举函数yield return


溶液2

这使用内另一个,它返回一个元件LINQ查询的另一个笨重构建体(事情是,它需要一个源 - I使用单元件串和丢弃的结果,但不是很干净)

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

是否有其他办法,我能做到这一点,可能无需SelectMany在查询中?

您可以将查询置于单独的方法中。

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

这不是真正的可扩展性,因为您必须为代码中要使用的每个查询创建一个新函数和一个类 –