循环返回AFTER退出(涉及IEnumerable)导致边界错误

Dan*_*elD 0 c# ienumerable search loops

我知道有很多方法可以对这只猫进行修饰,但是我遇到了一些基本的while循环问题,它们正常退出,但是在从父函数返回IEnumerable.ToList()时会返回(并增加计数器).基本上我要做的是搜索单个关键字的列表并过滤掉结果集,直到我只获得包含所有关键字的项目.

它退出循环,然后一旦命中返回,它就会返回到循环中,i变量增加另一个位置(高于count变量),因此broken [i]抛出一个极限之外的错误.

虽然我知道有更好的方法来执行搜索,但我也非常感兴趣为什么代码会返回到退出的循环,再次递增,并导致错误 - 以及如何修复它.这很奇怪,我以前从未见过这个.是因为.ToList强制枚举备份链吗?

码:

public IEnumerable<Item> GetItemsForSearch(string search=null)
    {
        ParserDataContext data = new ParserDataContext();            
        if (search != null && search != "")
        {                   
            string[] broken = search.Split(' ');                
            IEnumerable<Item> watches = data.Items.Where(x => x.Title.ToLower().Contains(broken[0].ToLower())).OrderByDescending(x => x.DateListed).ThenByDescending(x => x.ID);

            int i = 1;
            int count = broken.Count();
            while (i < count)
            {
                if (broken[i] != null)
                item = items.Where(x => x.Title.ToLower().Contains(broken[i].ToLower()));
                i++;
            }

            return items.Take(50).ToList();
        }
Run Code Online (Sandbox Code Playgroud)

}

das*_*ght 6

虽然我知道有更好的方法来执行搜索,但我也非常感兴趣为什么代码会返回到退出的循环,再次递增,并导致错误

第一件事是要了解实际发生的事情:代码不会回到已经退出的循环中,它只是i在错误的时间点使用变量的值,即在它增加之后.这是一个已知的功能:编译器捕获ibroken[i]直接,未做它的拷贝.

显然,这个功能已经足够烦人,微软可以在C#5.0中宣布修复这个错误的常见情况(特别是在foreach循环中关闭循环变量).

以及如何解决它.

对于C#4.5的用户,一个简单的解决方法是声明一个临时变量而不是使用i.编译器将捕获临时值,使行为符合您的预期:

while (i < count) {
    if (broken[i] != null) {
        var tmp = broken[i].ToLower();
           items = items.Where(x => x.Title.ToLower().Contains(tmp));
        }
        i++;
    }
}
Run Code Online (Sandbox Code Playgroud)

更好的方法是将所有破碎的单词收集在一起,然后在单个镜头中运行选择,如下所示:

var allBroken = broken.Where(b => b != null).Select(ToLower).ToList();
var items = items
    .Select(item => new {Item = item, Title = item.Title.ToLower()})
    .Where(pair => allBroken.Any(broken => pair.Title.Contains(broken)))
    .ToList();
Run Code Online (Sandbox Code Playgroud)