182 lines
6.6 KiB
Plaintext
182 lines
6.6 KiB
Plaintext
== Why is this an issue?
|
|
|
|
When you call `Any()`, it clearly communicates the code's intention, which is to check if the collection is empty. Using `Count() == 0` for this purpose is less direct and makes the code slightly more complex. However, there are some cases where special attention should be paid:
|
|
|
|
* if the collection is an `EntityFramework` or other ORM query, calling `Count()` will cause executing a potentially massive SQL query and could put a large overhead on the application database. Calling `Any()` will also connect to the database, but will generate much more efficient SQL.
|
|
* if the collection is part of a LINQ query that contains `Select()` statements that create objects, a large amount of memory could be unnecessarily allocated. Calling `Any()` will be much more efficient because it will execute fewer iterations of the enumerable.
|
|
|
|
== How to fix it
|
|
|
|
Prefer using `Any()` to test for emptiness over `Count()`.
|
|
|
|
=== Code examples
|
|
|
|
==== Noncompliant code example
|
|
|
|
[source,csharp,diff-id=1,diff-type=noncompliant]
|
|
----
|
|
private static bool HasContent(IEnumerable<string> strings)
|
|
{
|
|
return strings.Count() > 0; // Noncompliant
|
|
}
|
|
|
|
private static bool HasContent2(IEnumerable<string> strings)
|
|
{
|
|
return strings.Count() >= 1; // Noncompliant
|
|
}
|
|
|
|
private static bool IsEmpty(IEnumerable<string> strings)
|
|
{
|
|
return strings.Count() == 0; // Noncompliant
|
|
}
|
|
----
|
|
|
|
==== Compliant solution
|
|
|
|
[source,csharp,diff-id=1,diff-type=compliant]
|
|
----
|
|
private static bool HasContent(IEnumerable<string> strings)
|
|
{
|
|
return strings.Any();
|
|
}
|
|
|
|
private static bool HasContent2(IEnumerable<string> strings)
|
|
{
|
|
return strings.Any();
|
|
}
|
|
|
|
private static bool IsEmpty(IEnumerable<string> strings)
|
|
{
|
|
return !strings.Any();
|
|
}
|
|
----
|
|
|
|
== Resources
|
|
|
|
=== Benchmarks
|
|
|
|
[options="header"]
|
|
|===
|
|
| Method | Runtime | Mean | Standard Deviation
|
|
| Count | .NET 9.0 | 2,841.003 ns | 266.0238 ns
|
|
| Any | .NET 9.0 | 1.749 ns | 0.1242 ns
|
|
| Count | .NET Framework 4.8.1 | 71,125.275 ns | 731.0382 ns
|
|
| Any | .NET Framework 4.8.1 | 31.774 ns | 0.3196 ns
|
|
|===
|
|
|
|
==== Glossary
|
|
|
|
* https://en.wikipedia.org/wiki/Arithmetic_mean[Mean]
|
|
* https://en.wikipedia.org/wiki/Standard_deviation[Standard Deviation]
|
|
|
|
The results were generated by running the following snippet with https://github.com/dotnet/BenchmarkDotNet[BenchmarkDotNet]:
|
|
|
|
[source,csharp]
|
|
----
|
|
private IEnumerable<int> collection;
|
|
|
|
public const int N = 10_000;
|
|
|
|
[GlobalSetup]
|
|
public void GlobalSetup()
|
|
{
|
|
collection = Enumerable.Range(0, N).Select(x => N - x);
|
|
}
|
|
|
|
[Benchmark(Baseline = true)]
|
|
public bool Count() =>
|
|
collection.Count() > 0;
|
|
|
|
[Benchmark]
|
|
public bool Any() =>
|
|
collection.Any();
|
|
----
|
|
|
|
Hardware Configuration:
|
|
|
|
[source]
|
|
----
|
|
BenchmarkDotNet v0.14.0, Windows 10 (10.0.19045.5247/22H2/2022Update)
|
|
12th Gen Intel Core i7-12800H, 1 CPU, 20 logical and 14 physical cores
|
|
[Host] : .NET Framework 4.8.1 (4.8.9282.0), X64 RyuJIT VectorSize=256
|
|
.NET 9.0 : .NET 9.0.0 (9.0.24.52809), X64 RyuJIT AVX2
|
|
.NET Framework 4.8.1 : .NET Framework 4.8.1 (4.8.9282.0), X64 RyuJIT VectorSize=256
|
|
----
|
|
|
|
|
|
ifdef::env-github,rspecator-view[]
|
|
|
|
'''
|
|
== Implementation Specification
|
|
(visible only on this page)
|
|
|
|
=== Message
|
|
|
|
Use ".Any()" to test whether this "IEnumerable<XXX>" is empty or not.
|
|
|
|
'''
|
|
== Comments And Links
|
|
(visible only on this page)
|
|
|
|
=== on 20 May 2015, 15:31:39 Ann Campbell wrote:
|
|
\[~tamas.vajk] please fill in code samples.
|
|
|
|
Also, the description is a shallow modification of the Java version. The reasoning sounded plausible, but please verify that the efficiency argument is valid for C#.
|
|
|
|
=== on 22 May 2015, 11:15:59 Tamas Vajk wrote:
|
|
Added a small sample.
|
|
|
|
The performance reasoning is not 100% true:
|
|
|
|
If you call `Count()` on a `List<T>`, it will cast the list to an `ICollection<T>`, and call the `Count` property. Whereas an `Any()` on a `List<T>` will get the underlying `IEnumerator`, and call `GetNext()` on it. My feeling is that the second one is going to be slower, but I haven't done any measurements.
|
|
|
|
Also, an `IEnumerable` can be an abstraction on top of a database as well. In this case `Any()` and `Count()` are transformed to SQL, and the performance may depend on many things. It seems that `Count()` performs better for SQL Server and Entity Framework: 2nd answer of \http://stackoverflow.com/questions/305092/which-method-performs-better-any-vs-count-0. But the samples there use predicates in the `Any()` and `Count()`, which might also affect the performance.
|
|
|
|
So I would emphasize the intention and not the performance.
|
|
|
|
=== on 22 May 2015, 12:17:46 Ann Campbell wrote:
|
|
\[~tamas.vajk], your response makes me wonder whether we should write the opposite rule (in addition to this one? instead of this one?).
|
|
|
|
That aside, I've made some edits. Please double-check me.
|
|
|
|
=== on 22 May 2015, 12:40:40 Tamas Vajk wrote:
|
|
LGTM
|
|
|
|
I wouldn't write the opposite rule. The developer needs to know whatever he is doing. In case of an underlying database table, the queries always need performance profiling, so the developer will need to optimize that, and can suppress this warning if he finds that for performance reasons he needs to use `Count() > 0`.
|
|
|
|
For in-memory operations, the performance difference won't matter. Or if it does, then other micro-optimizations will have to be done as well.
|
|
|
|
=== on 22 May 2015, 13:32:37 Tamas Vajk wrote:
|
|
\[~ann.campbell.2] I've added a new noncompliant code sample
|
|
|
|
=== on 22 May 2015, 14:37:51 Ann Campbell wrote:
|
|
Okay [~tamas.vajk]
|
|
|
|
=== on 26 May 2015, 11:35:52 Tamas Vajk wrote:
|
|
Added the `Count() == 0` to the description and extended the code samples
|
|
|
|
=== on 26 May 2015, 14:03:18 Ann Campbell wrote:
|
|
\[~tamas.vajk] the description talks about comparing `.Count()` to 0, but one of the code samples illustrates a comparison to 1.
|
|
|
|
=== on 26 May 2015, 14:13:47 Tamas Vajk wrote:
|
|
\[~ann.campbell.2] You are right, I removed the comparison to `0`.
|
|
|
|
=== on 27 May 2015, 14:04:31 Ann Campbell wrote:
|
|
Thanks [~tamas.vajk]. I've merged the code blocks into one block each for Compliant and Noncompliant
|
|
|
|
=== on 1 Jun 2015, 14:30:42 Ann Campbell wrote:
|
|
I've updated the examples with `List<string>`. Please double-check me.
|
|
|
|
=== on 1 Jun 2015, 14:49:45 Tamas Vajk wrote:
|
|
\[~ann.campbell.2], I've changed the `List<string>` to `IEnumerable<string>`, because the `List` has a `Count` property, so calling the `Count()` on it might not be the best example.
|
|
|
|
=== on 1 Jun 2015, 17:49:45 Ann Campbell wrote:
|
|
okay, thanks [~tamas.vajk]
|
|
|
|
=== on 2 Mar 2017, 15:45:13 Valeri Hristov wrote:
|
|
\[~ann.campbell.2], I updated the description a bit, could you please review?
|
|
|
|
include::../comments-and-links.adoc[]
|
|
|
|
endif::env-github,rspecator-view[]
|