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));
}
}
我发现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() };
}
这不是真正的可扩展性,因为您必须为代码中要使用的每个查询创建一个新函数和一个类 –
我在想同样的事情,但只使用静态泛型迭代器方法。 –
@Ivan:作为一种类型,它更容易重用。 –
当然。只要它被封装在公共静态方法中,实现并不重要(可能是一个或另一个)。 MoreLinq的好候选人? :) –