强制 IEnumerable<T> 在不调用 .ToArray() 或 .ToList() 的情况下进行评估
Force IEnumerable<T> to evaluate without calling .ToArray() or .ToList()
如果我使用类似这样的方式查询 EF...
IEnumerable<FooBar> fooBars = db.FooBars.Where(o => o.SomeValue == something);
IIRC,这会在后台创建一个惰性求值的可迭代状态机,它还不包含任何结果;相反,它包含 "how" 的表达式以在需要时获取结果。
如果我想强制集合包含结果,我必须调用 .ToArray()
或 .ToList()
有没有办法在不调用 .ToArray()
或 .ToList();
的情况下强制 IEnumerable<T>
集合包含结果?
理由
我不知道 CLR 是否能够做到这一点,但本质上我想强行创建一个实现 IEnumerable<T>
接口的评估集合,但由 CLR 在后台实现,因此不是一个List<T>
或Array<T>
大概这是不可能的,因为我不知道有任何 CLR 功能可以在内存中创建实现 IEnumerable<T>
的评估集合
提案
比如说,我可以这样写:
var x = IEnumerable<FooBar> fooBars = db.FooBars
.Where(o => o.SomeValue == something)
.Evaluate(); // Does NOT return a "concrete" impl such as List<T> or Array<T>
Console.WriteLine(x.GetType().Name);
// eg. <EvaluatedEnumerable>e__123
您可以使用 foreach
循环:
foreach (var item in fooBars) { }
请注意,这会评估 fooBars
中的所有项目,但会立即丢弃结果。下次您 运行 相同的 foreach
循环或 .ToArray()
、.ToList()
时,枚举将再次计算。
Is there a way to force an IEnumerable<T>
collection to contain results without calling .ToArray()
or .ToList();
?
是的,但这可能不是您想要的:
IEnumerable<T> source = …;
IEnumerable<T> cached = new List<T>(source);
问题是,IEnumerable<T>
不是具体类型。它是表示项目序列的接口(合同)。可以有任何具体类型 "hiding behind" 这个接口;有些可能只代表一个查询,有些实际上将查询的项目保存在内存中。
如果你想强制计算你的序列,以便结果实际存储在物理内存中,你需要确保 IEnumerable<T>
背后的具体类型是一个保存结果的内存集合的评价。上面的代码示例就是这样做的。
我 运行 涉及的一个具体用例围绕着需要确保包装数据库查询的 IEnumerable 在将控制权返回给调用方法。但是结果太大而无法完全评估,因此 IEnumerable 支持流式传输。
internal class EagerEvaluator<T>
{
private readonly T _first;
private readonly IEnumerator<T> _enumerator;
private readonly bool _hasFirst;
public EagerEvaluator(IEnumerable<T> enumerable)
{
_enumerator = enumerable.GetEnumerator();
if (_enumerator.MoveNext())
{
_hasFirst = true;
_first = _enumerator.Current;
}
}
public IEnumerable<T> ToEnumerable()
{
if (_hasFirst)
{
yield return _first;
while (_enumerator.MoveNext())
{
yield return _enumerator.Current;
}
}
}
}
用法非常简单:
IEnumerable<FooBar> fooBars = new EagerEvaluator(fooBars).ToEnumerable()
另一个选项是:
<linq expression>.All( x => true);
我使用 Aggregate<T>()
来评估一个 IEnumerable<T>
有副作用:
private static IEnumerable<T> Evaluate<T>(IEnumerable<T> source)
=> source.Aggregate(Enumerable.Empty<T>(), (evaluated, s) => evaluated.Append(s));
如果我使用类似这样的方式查询 EF...
IEnumerable<FooBar> fooBars = db.FooBars.Where(o => o.SomeValue == something);
IIRC,这会在后台创建一个惰性求值的可迭代状态机,它还不包含任何结果;相反,它包含 "how" 的表达式以在需要时获取结果。
如果我想强制集合包含结果,我必须调用 .ToArray()
或 .ToList()
有没有办法在不调用 .ToArray()
或 .ToList();
的情况下强制 IEnumerable<T>
集合包含结果?
理由
我不知道 CLR 是否能够做到这一点,但本质上我想强行创建一个实现 IEnumerable<T>
接口的评估集合,但由 CLR 在后台实现,因此不是一个List<T>
或Array<T>
大概这是不可能的,因为我不知道有任何 CLR 功能可以在内存中创建实现 IEnumerable<T>
提案
比如说,我可以这样写:
var x = IEnumerable<FooBar> fooBars = db.FooBars
.Where(o => o.SomeValue == something)
.Evaluate(); // Does NOT return a "concrete" impl such as List<T> or Array<T>
Console.WriteLine(x.GetType().Name);
// eg. <EvaluatedEnumerable>e__123
您可以使用 foreach
循环:
foreach (var item in fooBars) { }
请注意,这会评估 fooBars
中的所有项目,但会立即丢弃结果。下次您 运行 相同的 foreach
循环或 .ToArray()
、.ToList()
时,枚举将再次计算。
Is there a way to force an
IEnumerable<T>
collection to contain results without calling.ToArray()
or.ToList();
?
是的,但这可能不是您想要的:
IEnumerable<T> source = …;
IEnumerable<T> cached = new List<T>(source);
问题是,IEnumerable<T>
不是具体类型。它是表示项目序列的接口(合同)。可以有任何具体类型 "hiding behind" 这个接口;有些可能只代表一个查询,有些实际上将查询的项目保存在内存中。
如果你想强制计算你的序列,以便结果实际存储在物理内存中,你需要确保 IEnumerable<T>
背后的具体类型是一个保存结果的内存集合的评价。上面的代码示例就是这样做的。
我 运行 涉及的一个具体用例围绕着需要确保包装数据库查询的 IEnumerable 在将控制权返回给调用方法。但是结果太大而无法完全评估,因此 IEnumerable 支持流式传输。
internal class EagerEvaluator<T>
{
private readonly T _first;
private readonly IEnumerator<T> _enumerator;
private readonly bool _hasFirst;
public EagerEvaluator(IEnumerable<T> enumerable)
{
_enumerator = enumerable.GetEnumerator();
if (_enumerator.MoveNext())
{
_hasFirst = true;
_first = _enumerator.Current;
}
}
public IEnumerable<T> ToEnumerable()
{
if (_hasFirst)
{
yield return _first;
while (_enumerator.MoveNext())
{
yield return _enumerator.Current;
}
}
}
}
用法非常简单:
IEnumerable<FooBar> fooBars = new EagerEvaluator(fooBars).ToEnumerable()
另一个选项是:
<linq expression>.All( x => true);
我使用 Aggregate<T>()
来评估一个 IEnumerable<T>
有副作用:
private static IEnumerable<T> Evaluate<T>(IEnumerable<T> source)
=> source.Aggregate(Enumerable.Empty<T>(), (evaluated, s) => evaluated.Append(s));