Create rule S6603: Prefer using the collection-specific TrueForAll method instead of the All extension (#1782)

This commit is contained in:
github-actions[bot] 2023-05-24 15:10:43 +02:00 committed by GitHub
parent 0ec4bbc964
commit b264b60d7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 183 additions and 0 deletions

View File

@ -0,0 +1,29 @@
=== Code examples
==== Noncompliant code example
[source,csharp,diff-id=1,diff-type=noncompliant]
----
public bool AreAllEven(List<int> data) =>
data.All(x => x % 2 == 0);
----
[source,csharp,diff-id=2,diff-type=noncompliant]
----
public bool AreAllEven(int[] data) =>
data.All(x => x % 2 == 0);
----
==== Compliant solution
[source,csharp,diff-id=1,diff-type=compliant]
----
public bool AreAllEven(List<int> data) =>
data.TrueForAll(x => x % 2 == 0);
----
[source,csharp,diff-id=2,diff-type=compliant]
----
public bool AreAllEven(int[] data) =>
Array.TrueForAll(data, x => x % 2 == 0);
----

View File

@ -0,0 +1,2 @@
{
}

View File

@ -0,0 +1,9 @@
include::../why-dotnet.adoc[]
include::../impact-dotnet.adoc[]
include::../how-dotnet.adoc[]
include::code.adoc[]
include::../resources-dotnet.adoc[]

View File

@ -0,0 +1,3 @@
== How to fix it
The `TrueForAll` method is defined on the collection class, and it has the same signature as the `All` extension method. The method can be replaced in place.

View File

@ -0,0 +1,3 @@
=== What is the potential impact?
We measured at least 4x improvement both in execution time. For more details see the `Benchmarks` section from the `More info` tab.

19
rules/S6603/metadata.json Normal file
View File

@ -0,0 +1,19 @@
{
"title": "The collection-specific \"TrueForAll\" method should be used instead of the \"All\" extension",
"type": "CODE_SMELL",
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"perfomance"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-6603",
"sqKey": "S6603",
"scope": "All",
"defaultQualityProfiles": ["Sonar way"],
"quickfix": "targeted"
}

View File

@ -0,0 +1,64 @@
== Resources
=== Documentation
* https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.trueforall[List<T>.TrueForAll(Predicate<T>)]
* https://learn.microsoft.com/en-us/dotnet/api/system.array.trueforall[Array.TrueForAll<T>(T[\], Predicate<T>)]
* https://learn.microsoft.com/en-us/dotnet/api/system.collections.immutable.immutablelist-1.trueforall[ImmutableList<T>.TrueForAll(Predicate<T>)]
* https://learn.microsoft.com/en-us/dotnet/api/system.collections.immutable.immutablelist-1.builder.trueforall[ImmutableList<T>.Builder.TrueForAll(Predicate<T>)]
* https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.all[Enumerable.All<TSource>]
=== Benchmarks
[options="header"]
|===
| Method | Runtime | Mean | StdDev | Ratio | Allocated
| TrueForAll | .NET 7.0 | 1.302 ms | 0.0027 ms | 0.21 | 1 B
| All | .NET 7.0 | 6.279 ms | 0.0181 ms | 1.00 | 40004 B
| TrueForAll | .NET Framework 4.6.2 | 1.105 ms | 0.0142 ms | 0.22 | -
| All | .NET Framework 4.6.2 | 4.968 ms | 0.0143 ms | 1.00 | 40128 B
|===
The results were generated by running the following snippet with https://github.com/dotnet/BenchmarkDotNet[BenchmarkDotNet]:
[source,csharp]
----
private List<int> data;
[Params(10_000)]
public int N { get; set; }
[GlobalSetup]
public void Setup() =>
data = Enumerable.Range(0, N).Select(x => 42).ToList();
[Benchmark]
public void TrueForAll()
{
for (var i = 0; i < N; i++)
{
_ = data.TrueForAll(x => x == 42); // List<T>.TrueForAll
}
}
[Benchmark(Baseline = true)]
public void All()
{
for (var i = 0; i < N; i++)
{
_ = data.All(x => x == 42); // Enumerable.All<TSource>
}
}
----
Hardware configuration:
[source]
----
BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.2846/22H2/2022Update)
12th Gen Intel Core i7-12800H, 1 CPU, 20 logical and 14 physical cores
.NET SDK=7.0.203
[Host] : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2
.NET 7.0 : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2
.NET Framework 4.6.2 : .NET Framework 4.8 (4.8.4614.0), X64 RyuJIT VectorSize=256
----

View File

@ -0,0 +1,33 @@
=== Code examples
==== Noncompliant code example
[source,vbnet,diff-id=1,diff-type=noncompliant]
----
Public Function AreAllEven(data As List(Of Integer)) As Boolean
Return data.All(Function(x) x Mod 2 = 0)
End Function
----
[source,vbnet,diff-id=2,diff-type=noncompliant]
----
Public Function AreAllEven(data As Integer()) As Boolean
Return data.All(Function(x) x Mod 2 = 0)
End Function
----
==== Compliant solution
[source,vbnet,diff-id=1,diff-type=compliant]
----
Public Function AreAllEven(data As List(Of Integer)) As Boolean
Return data.TrueForAll(Function(x) x Mod 2 = 0)
End Function
----
[source,vbnet,diff-id=2,diff-type=compliant]
----
Public Function AreAllEven(data As Integer()) As Boolean
Return Array.TrueForAll(data, Function(x) x Mod 2 = 0)
End Function
----

View File

@ -0,0 +1,2 @@
{
}

View File

@ -0,0 +1,9 @@
include::../why-dotnet.adoc[]
include::../impact-dotnet.adoc[]
include::../how-dotnet.adoc[]
include::code.adoc[]
include::../resources-dotnet.adoc[]

View File

@ -0,0 +1,10 @@
== Why is this an issue?
Both the `List.TrueForAll` method and the `IEnumerable.All` method can be used to check if all list elements satisfy a given condition in a collection. However, `List.TrueForAll` can be faster than `IEnumerable.All` for `List` objects. The performance difference may be minor for small collections, but for large collections, it can be noticeable.
*Applies to*
* https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.trueforall[List]
* https://learn.microsoft.com/en-us/dotnet/api/system.array.trueforall[Array]
* https://learn.microsoft.com/en-us/dotnet/api/system.collections.immutable.immutablelist-1.trueforall[ImmutableList]
* https://learn.microsoft.com/en-us/dotnet/api/system.collections.immutable.immutablelist-1.builder.trueforall[ImmutableList.Builder]