Elad Benda
Elad Benda

Reputation: 36654

Possible duplicate enumeration of IEnumerable

I have wrote the following code:

IEnumerable<string> blackListCountriesCodes = 
pair.Criterion.CountriesExceptions.Select(countryItem => countryItem.CountryCode);

IEnumerable<string> whiteListCountriesCodes = 
pair.Criterion.Countries.Select(countryItem => countryItem.CountryCode);

return (!blackListCountriesCodes.Contains(Consts.ALL.ToString()) &&
        !blackListCountriesCodes.Contains(country) &&
        (whiteListCountriesCodes.Contains(Consts.ALL.ToString()) ||
        whiteListCountriesCodes.Contains(country)));

resharper shows me a warning: Possible duplicate enumeration of IEnumerable

What does this mean? Why is this a warning?

Upvotes: 6

Views: 414

Answers (3)

Albin Sunnanbo
Albin Sunnanbo

Reputation: 47038

It means that you might calculate the content of the IEnumerable twice (or more). If this is expensive, like calling a database this is bad for performance. If the underlying source is a List<T> and there is a simple projection or something else non-expensive this is not a problem.

You could of course rewrite the expression to use the enumerables only once.

!blackListCountriesCodes.Contains(Consts.ALL.ToString())
    && !blackListCountriesCodes.Contains(country)

could be rewritten as

!blackListCountriesCodes
    .Where(blcc => blcc == Consts.ALL.ToString() || blcc == country).Any()

Upvotes: 1

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726599

This means that your code may enumerate the blackListCountriesCodes and whiteListCountriesCodes several times. Since LINQ uses deferred evaluation, this may cause slowness, especially when the pair has lots of data, and Where clauses are complex (it does not look like any of that applies in your situation, though).

You can eliminate the warning (and the alleged slowness) by "materializing" the enumerations into lists, like this:

var blackListCountriesCodes = 
    pair.Criterion.CountriesExceptions.Select(countryItem => countryItem.CountryCode).ToList();

var whiteListCountriesCodes = 
    pair.Criterion.Countries.Select(countryItem => countryItem.CountryCode).ToList();

Upvotes: 1

Justin Niessner
Justin Niessner

Reputation: 245429

LINQ Queries defer their execution until you do something with the results. In this case, calling Contains() twice on the same collection could cause the results to be enumerated twice which, depending on the query, could cause performance issues.

You can solve this by simply tacking a ToList() call on the end of your query which will force execution of the query and store the results a single time.

Upvotes: 9

Related Questions