4

我从 Reshaper 收到“ IEnumerable 的可能多重枚举”警告。如何处理它已经在另一个 SO question 中提出。不过,我的问题稍微具体一点,关于会弹出警告的各个地方。

我想知道的是 Resharper 给我这个警告是否正确。我主要担心的是警告出现在下面变量的所有实例users,在代码中用“ //Warn”表示。

我的代码正在收集要在网格中的网页上显示的信息。我正在使用服务器端分页,因为整个数据集可能长达数万或数十万行。我已经尽可能地注释了代码。

再次,请让我知道此代码是否容易受到多个枚举的影响。我的目标是在调用 ToList()之前对数据进行过滤和排序。这是正确的方法吗?

private List<UserRow> GetUserRows(UserFilter filter, int start, int limit,
                                  string sort, SortDirection dir, out int count)
{
    count = 0;

    // LINQ applies filter to Users object
    var users = (
            from u in _userManager.Users
            where filter.Check(u)
            select new UserRow
                        {
                            UserID = u.UserID,
                            FirstName = u.FirstName,
                            LastName = u.LastName,
                            // etc.
                        }
        );

    // LINQ orders by given sort
    if (!String.IsNullOrEmpty(sort))
    {
        if (sort == "UserID" && dir == SortDirection.ASC)
            users = users.OrderBy(u => u.UserID); //Warn
        else if (sort == "UserID" && dir == SortDirection.DESC)
            users = users.OrderByDescending(u => u.UserID); //Warn
        else if (sort == "FirstName" && dir == SortDirection.ASC)
            users = users.OrderBy(u => u.FirstName); //Warn
        else if (sort == "FirstName" && dir == SortDirection.DESC)
            users = users.OrderByDescending(u => u.FirstName); //Warn
        else if (sort == "LastName" && dir == SortDirection.ASC)
            users = users.OrderBy(u => u.LastName); //Warn
        else if (sort == "LastName" && dir == SortDirection.DESC)
            users = users.OrderByDescending(u => u.LastName); //Warn
        // etc.
    }
    else
    {
        users = users.Reverse(); //Warn
    }

    // Output variable
    count = users.Count(); //Warn

    // Guard case - shouldn't trigger
    if (limit == -1 || start == -1)
        return users.ToList(); //Warn

    // Pagination and ToList()
    return users.Skip((start / limit) * limit).Take(limit).ToList(); //Warn
}
4

2 回答 2

4

是的,ReSharper 是对的:count = users.Count();无条件枚举,然后如果 thelimit或 thestart不是负数1ToList则将再次枚举users

看来,一旦 ReSharper 确定某些内容有被多次枚举的风险,它就会使用多次枚举警告标记对相关项目的每个引用,即使它不是负责多次枚举的代码。这就是为什么您在这么多行上看到警告的原因。

更好的方法是添加一个单独的调用来设置计数。您可以在单独的语句中预先执行此操作,如下所示:

count = _userManager.Users.Count(u => filter.Check(u));

这样,您将能够保持users其预先枚举的状态,直到最终调用ToList.

于 2012-12-03T15:58:04.653 回答
1

希望您的警告是由对 的调用生成的Count,它确实运行了一个额外的查询。

limit == -1 || start == -1您可以ToList拨打电话然后从中获取的count情况,但在一般情况下,您无能为力 - 您正在进行两个查询,一个用于完整计数,一个用于项目的子集。

我很想知道修复特殊情况是否会导致警告消失。


编辑:由于这是 LINQ-to-objects,您可以用一个循环替换最后return一行,该foreach循环遍历整个集合对它们进行计数,但也可以动态构建受限的跳过/获取子列表,因此只迭代一次。

您还可以受益于仅(select new UserRow)在此foreach循环中和您的 special-case 之前进行ToList投影,而不是投影整个集合然后可能丢弃大部分对象。

var users = _userManager.Users.Where(u => filter.Check(u));

// Sort as above

List<UserRow> rtn;
if (limit == -1 || start == -1)
{
    rtn = users.Select(u => new UserRow { UserID = u.UserID, ... }).ToList();
    count = rtn.Length;
}
else
{
    int takeFrom = (start / limit) * limit;
    int forgetFrom = takeFrom + limit;
    count = 0;
    rtn = new List<UserRow>();
    foreach(var u in users)
    {
        if (count >= takeFrom && count < forgetFrom)
            rtn.Add(new UserRow { UserID = u.UserID, ... });
        count++;
    }
}
return rtn;
于 2012-12-03T15:58:57.807 回答