我从Reshaper收到“Possible Multiple Enumeration of IEnumerable”警告。如何处理它已经asked in another 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
}
答案 0 :(得分:4)
是的,ReSharper是对的:count = users.Count();
无条件枚举,如果limit
或start
不是否定1
,则ToList
将枚举再次users
。
看起来,一旦ReSharper决定某些内容有被多次枚举的风险,它会使用多个枚举警告标记对所讨论项目的每一个引用,即使它不是负责多次枚举的代码。这就是为什么你看到这么多行的警告。
更好的方法是添加单独的调用来设置计数。您可以在单独的声明中预先完成,如下所示:
count = _userManager.Users.Count(u => filter.Check(u));
通过这种方式,您可以将users
置于其预先列举的状态,直到最后一次调用ToList
。
答案 1 :(得分:1)
希望通过调用Count
生成您的警告,limit == -1 || start == -1
会运行额外的查询。
如果ToList
您可以进行count
来电,然后从那里获取return
,但在一般情况下,您无能为力做 - 你 做两个查询,一个用于完整计数,一个用于项目的子集。
我很想知道修理特殊情况是否会导致警告消失。
编辑:由于这是LINQ到对象,您可以将foreach
行替换为整个集合中的(select new UserRow)
循环计算它们,但也可以构建受限制的跳过/动态获取子列表,因此只迭代一次。
您还可以在此foreach
循环中,在您的特殊情况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;
,而不是预测整个集合,然后可能放弃您的大多数对象。
{{1}}