2017-08-10 109 views
2

函數的結果是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()

回答

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

我在想同樣的事情,但只使用靜態泛型迭代器方法。 –

+0

@Ivan:作爲一種類型,它更容易重用。 –

+0

當然。只要它被封裝在公共靜態方法中,實現並不重要(可能是一個或另一個)。 MoreLinq的好候選人? :) –

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

這使用延遲執行函數結果的枚舉函數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在查詢中?

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

這不是真正的可擴展性,因爲您必須爲代碼中要使用的每個查詢創建一個新函數和一個類 –