LINQ 以函数结果作为源延迟执行(例如 Console.ReadLine)
LINQ deferred execution with a function's result as source (e.g. Console.ReadLine)
函数的结果是 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()
那样调用终止方法。我想要的是仅当我将查询与 ToList()
或 Count()
等
一起使用时才执行 Console.WriteLine()
(或其位置的任何其他函数)
我找到了 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 查询构造,其中 returns 一个元素(问题是它需要一个源 - 我使用一个单元素字符串并丢弃结果但那不是真的很干净)
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
?
如果您不介意一些额外的基础设施,那也不算太糟糕 - 您可以创建一个 DeferredEnumerable<T>
class,它在每次请求迭代器时只执行给定的委托。然后,静态非泛型 class 可以帮助进行类型推断。完整示例:
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() };
}
函数的结果是 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()
那样调用终止方法。我想要的是仅当我将查询与 ToList()
或 Count()
等
Console.WriteLine()
(或其位置的任何其他函数)
我找到了 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 查询构造,其中 returns 一个元素(问题是它需要一个源 - 我使用一个单元素字符串并丢弃结果但那不是真的很干净)
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
?
如果您不介意一些额外的基础设施,那也不算太糟糕 - 您可以创建一个 DeferredEnumerable<T>
class,它在每次请求迭代器时只执行给定的委托。然后,静态非泛型 class 可以帮助进行类型推断。完整示例:
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() };
}