4

Reshaperから「 IEnumerableの複数の列挙の可能性」という警告が表示されます。それをどのように処理するかは、別のSOの質問ですでに尋ねられています。私の質問はもう少し具体的ですが、警告がポップアップするさまざまな場所についてです。

私が疑問に思っているのは、Resharperが私にこの警告を与えるのが正しいかどうかです。私の主な懸念は、コードで「」で示されている以下の変数のすべてのインスタンスで警告が発生することです。users//Warn

私のコードは、グリッド内のWebページに表示される情報を収集しています。データセット全体の長さが数万から数十万行になる可能性があるため、サーバー側のページングを使用しています。私は可能な限りコードにコメントしました。

繰り返しになりますが、このコードが複数の列挙の影響を受けやすいかどうかを教えてください。私の目標は、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();無条件に列挙し、limitまたはstartが負1でない場合は、ToListが再び列挙usersします。

ReSharperは、何かが複数回列挙されるリスクがあると判断すると、複数の列挙を担当するコードではない場合でも、問題のアイテムへのすべての参照に複数の列挙の警告のフラグを立てます。そのため、非常に多くの行に警告が表示されます。

より良いアプローチは、カウントを設定するための別の呼び出しを追加することです。次のように、別のステートメントで事前に行うことができます。

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

usersこのようにして、の最後の呼び出しまで、事前に列挙された状態のままにすることができますToList

于 2012-12-03T15:58:04.653 に答える
1

警告は、への呼び出しによって生成されることを願っCountています。これは、追加のクエリを実行します。

呼び出しを行ってからlimit == -1 || start == -1取得できる場合でも、一般的な場合は何もできません。1つはフルカウント用、もう1つはアイテムのサブセット用の2つのクエリを実行します。ToListcount

特殊なケースを修正すると警告が消えるかどうかを知りたいと思います。


編集:これはLINQ-to-objectsであるため、最後のreturn行を、foreachそれらをカウントするコレクション全体を通過するループに置き換えることができますが、制限されたスキップ/テイクサブリストを動的に構築し、1回だけ繰り返すことができます。

また、コレクション全体を投影してからオブジェクトの大部分を破棄する可能性があるのではなく(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;
于 2012-12-03T15:58:57.807 に答える