使用.First()和.Count()打破了IEnumerable?

时间:2012-03-12 09:31:53

标签: c# .net list ienumerable

这是我的代码:

protected IEnumerable<Hotel> Hotels;
protected Hotel Hotel;

Hotels = from Hotel hotel in new Hotels()
         select hotel;

Response.Write("RES " + Hotels.Count() + "<br />");
if (Hotels.Count() > 0)
{
    Hotel = Hotels.First();
}
Response.Write("RES " + Hotels.Count() + "<br />");
Response.Write("RES " + Hotels.Count() + "<br />");

嗯,酒店有1项。但结果是:

RES 1
RES 0
RES 1

为什么呢?似乎.First()与迭代器混淆了?如何使用IEnumberable修复它? (不使用其他类型的列表,我需要IEnumerable)。

4 个答案:

答案 0 :(得分:3)

static void Main(string[] args)
{
    IEnumerable<String> Hotels = new List<String>{"sdsfsdf"};
    String Hotel;

    Console.WriteLine("RES " + Hotels.Count());
    if (Hotels.Count() > 0)
    {
        Hotel = Hotels.First();
    }
    Console.WriteLine("RES " + Hotels.Count());
    Console.WriteLine("RES " + Hotels.Count());
}

打印

RES 1
RES 1
RES 1

正如预期的那样,你是如何填充可枚举的,以及你创建它的类型是什么?

如果你通过未执行的查询来填充这个,你可能会得到这样的奇怪行为,因为它会选择第一个用法(在这种情况下是First())。

答案 1 :(得分:3)

这里没有魔力。看起来Hotels集合中的项目数量在时间上有所变化,可能是一些带有自定义执行的LINQ查询,甚至可能是LINQ-to-SQL。

请显示填写酒店的完整代码。

答案 2 :(得分:1)

“First”方法可能会使迭代器前进到指向第二个元素。第一次调用“Count”然后完成对一个元素序列的迭代,随后的count调用将重新开始迭代。 Count将始终迭代整个序列,因此它是幂等的。您需要一种方法在Hotels.Reset()之后重置枚举器First(),以使Count()行为正确。

最简单的事情可能是自己制作Reset扩展名,但我无法重现您的问题。如果你有一些系统生成的数据库迭代器,它可能不会自动重置,你可以使用下面的代码。它只是利用你自己的发现,一个Count()重置迭代器。它将O(n)完成。

static class Extensions
{
    public static void Reset<T>(this IEnumerable<T> toReset )
    {
        if (toReset != null)
        {
            int i = toReset.Count();
        }
    }
}

但我无法重现你的问题:

下面的代码给出了正确的结果。你有自己的枚举器或集合吗?

static void Main(string[] args)
{
    var Response = System.Console.Out;

    var Hotels = new[]{1, 2, 3, 4};
    var Hotel = 0;

    Response.Write("RES " + Hotels.Count() + "<br />");
    if (Hotels.Count() > 0)
    {
        Hotel = Hotels.First();
    }
    Response.Write("RES " + Hotels.Count() + "<br />");
    Response.Write("RES " + Hotels.Count() + "<br />");

    Console.WriteLine( "Hotel: " + Hotel);
}

答案 3 :(得分:1)

根据底层实现,无法以任何方式重新枚举IEnumerable以返回相同的值。要了解原因,请考虑此示例;

static void Main(string[] args)
{
    IEnumerable<int> bop = RandomSequence();
    Console.WriteLine(bop.Count());
    Console.WriteLine(bop.Count());
}

private static int _seed = 0;
static IEnumerable<int> RandomSequence()
{
    var random = new Random(_seed++);
    int randomNumber;
    while ((randomNumber = random.Next(100)) != 0)
        yield return randomNumber;
} 

这是一个完全有效的IEnumerable,对Count()的两次调用将计算为两个不同的伪随机值。原因是Count()重新枚举相同的IEnumerable并生成完全不同的随机序列。

如果你想要可重复的枚举,你需要在枚举上调用ToList()ToArray来存储结果,并且每次都以相同的方式从列表/数组中进行所有枚举。