C# 使用.First()和.Count()会破坏IEnumerable吗?

C# 使用.First()和.Count()会破坏IEnumerable吗?,c#,.net,list,ienumerable,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.Fir

这是我的代码:

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 />");
为什么??似乎
.First()
与迭代器混淆了?如何使用IEnumberable修复它?(不使用其他类型的列表,我需要IEnumerable)。

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

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

static class Extensions
{
    public static void Reset<T>(this IEnumerable<T> toReset )
    {
        if (toReset != null)
        {
            int i = toReset.Count();
        }
    }
}
静态类扩展
{
公共静态无效重置(此IEnumerable重置)
{
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);
}
static void Main(字符串[]args)
{
var响应=System.Console.Out;
var Hotels=新[]{1,2,3,4};
var酒店=0;
Response.Write(“RES”+Hotels.Count()+”
); 如果(Hotels.Count()>0) { Hotel=Hotels.First(); } Response.Write(“RES”+Hotels.Count()+”
); Response.Write(“RES”+Hotels.Count()+”
); 控制台。WriteLine(“酒店:”+酒店); }
正如预期的那样,您是如何填充枚举表的,以及创建它的类型是什么


如果您是通过一个未执行的可查询项来填充此内容,您可能会得到像这样的奇怪行为,因为它将选择第一个使用(即本例中的
first()
)。

这里没有魔法。看起来
Hotels
集合中的项目数量随时间而变化,可能是执行不同的LINQ查询,甚至可能是LINQ to SQL


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

根据基础实现,重新枚举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;
} 
static void Main(字符串[]args)
{
IEnumerable bop=随机序列();
Console.WriteLine(bop.Count());
Console.WriteLine(bop.Count());
}
私有静态int_seed=0;
静态IEnumerable随机序列()
{
var random=新随机(_seed++);
整数随机数;
而((randomNumber=random.Next(100))!=0)
收益率随机数;
} 
这是一个完全有效的IEnumerable,对Count()的两个调用将计算为两个不同的伪随机值。原因是Count()重新枚举相同的IEnumerable并生成完全不同的随机序列


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

酒店是如何填充的&底层存储是什么,例如,它是否实际引用了列表?行为取决于酒店的具体情况。如果它包含一些对数据源的引用/连接,并且这些引用/连接可以更改,那么很明显,该值可以在调用Count之间更改,或者如果它是用yield-return实现的,那么它可以做任何事情。尝试对其调用ToArray(),然后对从ToArray()返回的值进行计数等,这样您就知道您有一个集合的固定快照。这可能是Hotels的实际实现有问题。例如,它适用于列表。哪个类实现了IEnumerable?如何初始化该
Hotels
变量?它适用于列表数组等。顺便说一句,您可以使用
FirstOrDefault()
扩展方法,而不是if构造。刚刚在控制台应用程序中使用列表对其进行测试,它会按预期返回1、1、1。IEnumerable上没有.Reset()方法,已选中:(不,它在枚举器上。您在
Hotels
中使用的是哪个集合?--我无法使用数组重现您的错误。markzzz,我想您使用了一些奇怪的对象作为IEnumerable。可能是您自己的实现把事情搞砸了。然后我认为底层数据源会随着时间的推移而变化,因此会产生不同的结果。。。你能发布你的linq语句吗?这也是直接来自数据库的吗?在你的linq末尾添加一个.ToArray()。这将迫使查询在.First()之前填充到内存中,这意味着它将针对.net对象执行……添加.ToArray()有效!但是,为什么会出现这种行为?你能解释一下吗?@markzzz:ToArray()执行基础LINQ查询并返回集合本身,因此Count()将引用始终指向同一个项列表是的,当然,发生的是您正在执行每个顺序语句,而不是IQuerable。发生的是,查询提供程序决定如何将其转换为SQL,我想象当您第一次调用时,它会将您的实体作为查询而不是可枚举的,从而清除基本集合
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
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;
}