强制IEnumerable 无需调用.ToArray()或.ToList()进行评估

问题描述 投票:4回答:4

如果我使用这样的东西查询EF ...

IEnumerable<FooBar> fooBars = db.FooBars.Where(o => o.SomeValue == something);

IIRC,这在后台创建了一个懒惰评估,可迭代的状态机,但尚未包含任何结果;相反,它包含一个表达式“如何”在需要时获得结果。

如果我想强制集合包含结果,我必须调用.ToArray().ToList()

有没有办法迫使IEnumerable<T>集合包含结果而不调用.ToArray().ToList();

合理

我不知道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
c# entity-framework linq ienumerable lazy-evaluation
4个回答
6
投票

有没有办法迫使IEnumerable<T>集合包含结果而不调用.ToArray().ToList();

是的,但它可能不是你想要的:

IEnumerable<T> source = …;
IEnumerable<T> cached = new List<T>(source);

问题是,IEnumerable<T>不是具体类型。它是表示项目序列的接口(契约)。可以有任何具体类型“隐藏”此界面;有些可能只代表一个查询,其他人实际上在内存中保存查询的项目。

如果要强制评估序列以使结果实际存储在物理内存中,则需要确保IEnumerable<T>背后的具体类型是包含评估结果的内存中集合。上面的代码示例就是这样做的。


4
投票

你可以使用foreach循环:

foreach (var item in fooBars) { }

请注意,这将评估fooBars中的所有项目,但会立即丢弃结果。下次运行相同的foreach循环或.ToArray().ToList()时,将再次评估枚举。


1
投票

我遇到的一个具体用例围绕需要确保在将控制权返回给调用方法之前,包装数据库查询的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()

0
投票

另一种选择是:

<linq expression>.All( x => true);
© www.soinside.com 2019 - 2024. All rights reserved.