From 665c6cf5f12ac6b68cc236db32725fe90fe2dc91 Mon Sep 17 00:00:00 2001 From: Antao Almada Date: Thu, 12 Oct 2023 14:47:43 +0100 Subject: [PATCH] Remove HLQ010 rule --- .../HLQ010_UseForLoop.ValueTypeEnumerator.cs | 70 ---------------- .../HLQ010_UseForLoopAnalyzerTests.cs | 56 ------------- .../HLQ010/Diagnostic/ArraySegment.cs | 14 ---- .../TestData/HLQ010/NoDiagnostic/Array.cs | 14 ---- .../HLQ010/NoDiagnostic/Dictionary.cs | 15 ---- .../HLQ010/NoDiagnostic/ImmutableArray.cs | 14 ---- .../TestData/HLQ010/NoDiagnostic/List.cs | 15 ---- .../HLQ010/NoDiagnostic/ReadOnlySpan.cs | 14 ---- .../TestData/HLQ010/NoDiagnostic/Span.cs | 14 ---- NetFabric.Hyperlinq.Analyzer.sln | 1 - .../AnalyzerReleases.Shipped.md | 1 - .../Analyzers/HLQ010_UseForLoopAnalyzer.cs | 61 -------------- NetFabric.Hyperlinq.Analyzer/DiagnosticIds.cs | 1 - README.md | 1 - docs/reference/HLQ010_UseForLoop.md | 81 ------------------- 15 files changed, 372 deletions(-) delete mode 100644 NetFabric.Hyperlinq.Analyzer.Benchmarks/HLQ010_UseForLoop.ValueTypeEnumerator.cs delete mode 100644 NetFabric.Hyperlinq.Analyzer.UnitTests/HLQ010_UseForLoopAnalyzerTests.cs delete mode 100644 NetFabric.Hyperlinq.Analyzer.UnitTests/TestData/HLQ010/Diagnostic/ArraySegment.cs delete mode 100644 NetFabric.Hyperlinq.Analyzer.UnitTests/TestData/HLQ010/NoDiagnostic/Array.cs delete mode 100644 NetFabric.Hyperlinq.Analyzer.UnitTests/TestData/HLQ010/NoDiagnostic/Dictionary.cs delete mode 100644 NetFabric.Hyperlinq.Analyzer.UnitTests/TestData/HLQ010/NoDiagnostic/ImmutableArray.cs delete mode 100644 NetFabric.Hyperlinq.Analyzer.UnitTests/TestData/HLQ010/NoDiagnostic/List.cs delete mode 100644 NetFabric.Hyperlinq.Analyzer.UnitTests/TestData/HLQ010/NoDiagnostic/ReadOnlySpan.cs delete mode 100644 NetFabric.Hyperlinq.Analyzer.UnitTests/TestData/HLQ010/NoDiagnostic/Span.cs delete mode 100644 NetFabric.Hyperlinq.Analyzer/Analyzers/HLQ010_UseForLoopAnalyzer.cs delete mode 100644 docs/reference/HLQ010_UseForLoop.md diff --git a/NetFabric.Hyperlinq.Analyzer.Benchmarks/HLQ010_UseForLoop.ValueTypeEnumerator.cs b/NetFabric.Hyperlinq.Analyzer.Benchmarks/HLQ010_UseForLoop.ValueTypeEnumerator.cs deleted file mode 100644 index 1ad4fc9..0000000 --- a/NetFabric.Hyperlinq.Analyzer.Benchmarks/HLQ010_UseForLoop.ValueTypeEnumerator.cs +++ /dev/null @@ -1,70 +0,0 @@ -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Configs; - -namespace NetFabric.Hyperlinq.Analyzer.Benchmarks; - -[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] -[CategoriesColumn] -public class HLQ010_UseForLoop_ValueTypeEnumerator -{ - ArraySegment arraySegment; - Dictionary? dictionary; - - [Params(100, 10_000)] - public int Count { get; set; } - - [GlobalSetup] - public void GlobalSetup() - { - var array = Enumerable.Range(0, Count).ToArray(); - arraySegment = new ArraySegment(array); - dictionary = array.ToDictionary(item => item); - } - - [BenchmarkCategory("ArraySegment")] - [Benchmark(Baseline = true)] - public int ArraySegment_Foreach() - { - var sum = 0; - foreach (var item in arraySegment) - sum += item; - return sum; - } - - [BenchmarkCategory("ArraySegment")] - [Benchmark] - public int ArraySegment_For() - { - var sum = 0; - for (var index = 0; index < arraySegment.Count; index++) - { - var item = arraySegment[index]; - sum += item; - } - return sum; - } - - - [BenchmarkCategory("Dictionary")] - [Benchmark(Baseline = true)] - public int Dictionary_Foreach() - { - var sum = 0; - foreach (var item in dictionary!) - sum += item.Value; - return sum; - } - - [BenchmarkCategory("Dictionary")] - [Benchmark] - public int Dictionary_For() - { - var sum = 0; - for (var index = 0; index < dictionary!.Count; index++) - { - var item = dictionary![index]; - sum += item; - } - return sum; - } -} diff --git a/NetFabric.Hyperlinq.Analyzer.UnitTests/HLQ010_UseForLoopAnalyzerTests.cs b/NetFabric.Hyperlinq.Analyzer.UnitTests/HLQ010_UseForLoopAnalyzerTests.cs deleted file mode 100644 index 51e4ea2..0000000 --- a/NetFabric.Hyperlinq.Analyzer.UnitTests/HLQ010_UseForLoopAnalyzerTests.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; -using System.IO; -using System.Linq; -using TestHelper; -using Xunit; - -namespace NetFabric.Hyperlinq.Analyzer.UnitTests -{ - public class UseForLoopAnalyzerTests : DiagnosticVerifier - { - protected override DiagnosticAnalyzer? GetCSharpDiagnosticAnalyzer() => - new UseForLoopAnalyzer(); - - protected override DiagnosticAnalyzer? GetBasicDiagnosticAnalyzer() - => null; - - [Theory] - [InlineData("TestData/HLQ010/NoDiagnostic/Array.cs")] - [InlineData("TestData/HLQ010/NoDiagnostic/Span.cs")] - [InlineData("TestData/HLQ010/NoDiagnostic/List.cs")] - [InlineData("TestData/HLQ010/NoDiagnostic/ReadOnlySpan.cs")] - [InlineData("TestData/HLQ010/NoDiagnostic/Dictionary.cs")] - [InlineData("TestData/HLQ010/NoDiagnostic/ImmutableArray.cs")] - public void Verify_NoDiagnostics(string path) - { - var paths = new[] - { - path, - }; - VerifyCSharpDiagnostic(paths.Select(path => File.ReadAllText(path)).ToArray()); - } - - [Theory] - [InlineData("TestData/HLQ010/Diagnostic/ArraySegment.cs", 10, 34)] - public void Verify_Diagnostic(string path, int line, int column) - { - var paths = new[] - { - path, - }; - var sources = paths.Select(path => File.ReadAllText(path)).ToArray(); - var expected = new DiagnosticResult - { - Id = "HLQ010", - Message = "Consider using 'for' instead of 'foreach' for iterating over collections featuring an indexer", - Severity = DiagnosticSeverity.Warning, - Locations = new[] { - new DiagnosticResultLocation("Test0.cs", line, column) - }, - }; - - VerifyCSharpDiagnostic(paths.Select(path => File.ReadAllText(path)).ToArray(), expected); - } - } -} diff --git a/NetFabric.Hyperlinq.Analyzer.UnitTests/TestData/HLQ010/Diagnostic/ArraySegment.cs b/NetFabric.Hyperlinq.Analyzer.UnitTests/TestData/HLQ010/Diagnostic/ArraySegment.cs deleted file mode 100644 index cacaaa6..0000000 --- a/NetFabric.Hyperlinq.Analyzer.UnitTests/TestData/HLQ010/Diagnostic/ArraySegment.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace HLQ010.NoDiagnostic.ArraySegment -{ - partial class C - { - void Method() - { - var source = new ArraySegment(); - foreach (var item in source) - Console.WriteLine(item); - } - } -} diff --git a/NetFabric.Hyperlinq.Analyzer.UnitTests/TestData/HLQ010/NoDiagnostic/Array.cs b/NetFabric.Hyperlinq.Analyzer.UnitTests/TestData/HLQ010/NoDiagnostic/Array.cs deleted file mode 100644 index c7e75a4..0000000 --- a/NetFabric.Hyperlinq.Analyzer.UnitTests/TestData/HLQ010/NoDiagnostic/Array.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace HLQ010.NoDiagnostic.Array -{ - partial class C - { - void Method() - { - var source = new int[0]; - foreach (var item in source) - Console.WriteLine(item); - } - } -} diff --git a/NetFabric.Hyperlinq.Analyzer.UnitTests/TestData/HLQ010/NoDiagnostic/Dictionary.cs b/NetFabric.Hyperlinq.Analyzer.UnitTests/TestData/HLQ010/NoDiagnostic/Dictionary.cs deleted file mode 100644 index 1e81d79..0000000 --- a/NetFabric.Hyperlinq.Analyzer.UnitTests/TestData/HLQ010/NoDiagnostic/Dictionary.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace HLQ010.NoDiagnostic.Dictionary -{ - partial class C - { - void Method() - { - var source = new Dictionary(); - foreach (var item in source) - Console.WriteLine(item); - } - } -} diff --git a/NetFabric.Hyperlinq.Analyzer.UnitTests/TestData/HLQ010/NoDiagnostic/ImmutableArray.cs b/NetFabric.Hyperlinq.Analyzer.UnitTests/TestData/HLQ010/NoDiagnostic/ImmutableArray.cs deleted file mode 100644 index 6d6c14d..0000000 --- a/NetFabric.Hyperlinq.Analyzer.UnitTests/TestData/HLQ010/NoDiagnostic/ImmutableArray.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace HLQ010.NoDiagnostic.ImmutableArray -{ - partial class C - { - void Method() - { - var source = System.Collections.Immutable.ImmutableArray.Create(System.Array.Empty()); - foreach (var item in source) - Console.WriteLine(item); - } - } -} diff --git a/NetFabric.Hyperlinq.Analyzer.UnitTests/TestData/HLQ010/NoDiagnostic/List.cs b/NetFabric.Hyperlinq.Analyzer.UnitTests/TestData/HLQ010/NoDiagnostic/List.cs deleted file mode 100644 index 5e3eb59..0000000 --- a/NetFabric.Hyperlinq.Analyzer.UnitTests/TestData/HLQ010/NoDiagnostic/List.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace HLQ010.Diagnostic.List -{ - partial class C - { - void Method() - { - var source = new List(); - foreach (var item in source) - Console.WriteLine(item); - } - } -} diff --git a/NetFabric.Hyperlinq.Analyzer.UnitTests/TestData/HLQ010/NoDiagnostic/ReadOnlySpan.cs b/NetFabric.Hyperlinq.Analyzer.UnitTests/TestData/HLQ010/NoDiagnostic/ReadOnlySpan.cs deleted file mode 100644 index 1a632ff..0000000 --- a/NetFabric.Hyperlinq.Analyzer.UnitTests/TestData/HLQ010/NoDiagnostic/ReadOnlySpan.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace HLQ010.NoDiagnostic.ReadOnlySpan -{ - partial class C - { - void Method() - { - var source = (ReadOnlySpan)new int[0].AsSpan(); - foreach (var item in source) - Console.WriteLine(item); - } - } -} diff --git a/NetFabric.Hyperlinq.Analyzer.UnitTests/TestData/HLQ010/NoDiagnostic/Span.cs b/NetFabric.Hyperlinq.Analyzer.UnitTests/TestData/HLQ010/NoDiagnostic/Span.cs deleted file mode 100644 index 03de3e2..0000000 --- a/NetFabric.Hyperlinq.Analyzer.UnitTests/TestData/HLQ010/NoDiagnostic/Span.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace HLQ010.NoDiagnostic.Span -{ - partial class C - { - void Method() - { - var source = new int[0].AsSpan(); - foreach (var item in source) - Console.WriteLine(item); - } - } -} diff --git a/NetFabric.Hyperlinq.Analyzer.sln b/NetFabric.Hyperlinq.Analyzer.sln index 48f1a42..0a485f3 100644 --- a/NetFabric.Hyperlinq.Analyzer.sln +++ b/NetFabric.Hyperlinq.Analyzer.sln @@ -32,7 +32,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Documentation", "Documentat docs\reference\HLQ007_NonDisposableEnumerator.md = docs\reference\HLQ007_NonDisposableEnumerator.md docs\reference\HLQ008_ReadOnlyRefEnumerable.md = docs\reference\HLQ008_ReadOnlyRefEnumerable.md docs\reference\HLQ009_RemoveOptionalMethods.md = docs\reference\HLQ009_RemoveOptionalMethods.md - docs\reference\HLQ010_UseForLoop.md = docs\reference\HLQ010_UseForLoop.md docs\reference\HLQ011_ReadOnlyEnumeratorField.md = docs\reference\HLQ011_ReadOnlyEnumeratorField.md docs\reference\HLQ012_UseCollectionsMarshalAsSpan.md = docs\reference\HLQ012_UseCollectionsMarshalAsSpan.md docs\reference\HLQ013_UseForEachLoop.md = docs\reference\HLQ013_UseForEachLoop.md diff --git a/NetFabric.Hyperlinq.Analyzer/AnalyzerReleases.Shipped.md b/NetFabric.Hyperlinq.Analyzer/AnalyzerReleases.Shipped.md index 587b7f5..e3501ba 100644 --- a/NetFabric.Hyperlinq.Analyzer/AnalyzerReleases.Shipped.md +++ b/NetFabric.Hyperlinq.Analyzer/AnalyzerReleases.Shipped.md @@ -12,7 +12,6 @@ HLQ006 | Performance | Warning | HLQ006_GetEnumeratorReturnTypeAnalyzer, [Docu HLQ007 | Performance | Warning | HLQ007_NonDisposableEnumeratorAnalyzer, [Documentation](https://github.com/NetFabric/NetFabric.Hyperlinq.Analyzer/blob/master/docs/reference/HLQ007_NonDisposableEnumerator.md) HLQ008 | Performance | Info | HLQ008_ReadOnlyRefEnumerableAnalyzer, [Documentation](https://github.com/NetFabric/NetFabric.Hyperlinq.Analyzer/blob/master/docs/reference/HLQ008_ReadOnlyRefEnumerable.md) HLQ009 | Performance | Info | HLQ009_RemoveOptionalMethodsAnalyzer, [Documentation](https://github.com/NetFabric/NetFabric.Hyperlinq.Analyzer/blob/master/docs/reference/HLQ009_RemoveOptionalMethods.md) -HLQ010 | Performance | Warning | HLQ010_UseForLoopAnalyzer, [Documentation](https://github.com/NetFabric/NetFabric.Hyperlinq.Analyzer/blob/master/docs/reference/HLQ010_UseForLoop.md) HLQ011 | Compiler | Error | HLQ011_ReadOnlyEnumeratorFieldAnalyzer, [Documentation](https://github.com/NetFabric/NetFabric.Hyperlinq.Analyzer/blob/master/docs/reference/HLQ011_ReadOnlyEnumeratorField.md) HLQ012 | Performance | Warning | HLQ012_UseCollectionsMarshalAsSpanAnalyzer, [Documentation](https://github.com/NetFabric/NetFabric.Hyperlinq.Analyzer/blob/master/docs/reference/HLQ012_UseCollectionsMarshalAsSpan.md) HLQ013 | Performance | Warning | HLQ013_UseForEachLoopAnalyzer, [Documentation](https://github.com/NetFabric/NetFabric.Hyperlinq.Analyzer/blob/master/docs/reference/HLQ013_UseForEachLoop.md) diff --git a/NetFabric.Hyperlinq.Analyzer/Analyzers/HLQ010_UseForLoopAnalyzer.cs b/NetFabric.Hyperlinq.Analyzer/Analyzers/HLQ010_UseForLoopAnalyzer.cs deleted file mode 100644 index 2c768d0..0000000 --- a/NetFabric.Hyperlinq.Analyzer/Analyzers/HLQ010_UseForLoopAnalyzer.cs +++ /dev/null @@ -1,61 +0,0 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; -using NetFabric.CodeAnalysis; -using System.Collections.Immutable; - -namespace NetFabric.Hyperlinq.Analyzer -{ - [DiagnosticAnalyzer(LanguageNames.CSharp)] - public sealed class UseForLoopAnalyzer : DiagnosticAnalyzer - { - const string DiagnosticId = DiagnosticIds.UseForLoopId; - - static readonly LocalizableString Title = - new LocalizableResourceString(nameof(Resources.UseForLoop_Title), Resources.ResourceManager, typeof(Resources)); - static readonly LocalizableString MessageFormat = - new LocalizableResourceString(nameof(Resources.UseForLoop_MessageFormat), Resources.ResourceManager, typeof(Resources)); - static readonly LocalizableString Description = - new LocalizableResourceString(nameof(Resources.UseForLoop_Description), Resources.ResourceManager, typeof(Resources)); - const string Category = "Performance"; - - static readonly DiagnosticDescriptor Rule = - new(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, - isEnabledByDefault: true, description: Description, - helpLinkUri: "https://github.com/NetFabric/NetFabric.Hyperlinq.Analyzer/tree/master/docs/reference/HLQ010_UseForLoop.md"); - - public override ImmutableArray SupportedDiagnostics => - ImmutableArray.Create(Rule); - - public override void Initialize(AnalysisContext context) - { - context.EnableConcurrentExecution(); - context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); - context.RegisterSyntaxNodeAction(AnalyzeForEachStatement, SyntaxKind.ForEachStatement); - } - - static void AnalyzeForEachStatement(SyntaxNodeAnalysisContext context) - { - var foreachStatement = (ForEachStatementSyntax)context.Node; - - var expressionType = context.SemanticModel.GetTypeInfo(foreachStatement.Expression).Type; - if (expressionType is null) - return; - - // if foreach uses indexer for this type then let it be - if (expressionType.IsForEachOptimized()) - return; - - // if it's List then should be using foreach with CollectionsMarshal.AsSpan() - if (expressionType.IsList(out _)) - return; - - if (expressionType.IsIndexable(out _)) - { - var diagnostic = Diagnostic.Create(Rule, foreachStatement.Expression.GetLocation()); - context.ReportDiagnostic(diagnostic); - } - } - } -} \ No newline at end of file diff --git a/NetFabric.Hyperlinq.Analyzer/DiagnosticIds.cs b/NetFabric.Hyperlinq.Analyzer/DiagnosticIds.cs index e5e3052..f145f09 100644 --- a/NetFabric.Hyperlinq.Analyzer/DiagnosticIds.cs +++ b/NetFabric.Hyperlinq.Analyzer/DiagnosticIds.cs @@ -11,7 +11,6 @@ static class DiagnosticIds public const string NonDisposableEnumeratorId = "HLQ007"; public const string ReadOnlyRefEnumerableId = "HLQ008"; public const string RemoveOptionalMethodsId = "HLQ009"; - public const string UseForLoopId = "HLQ010"; public const string ReadOnlyEnumeratorFieldId = "HLQ011"; public const string UseCollectionsMarshalAsSpanId = "HLQ012"; public const string UseForEachLoopId = "HLQ013"; diff --git a/README.md b/README.md index 1a4200f..c3220e5 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,6 @@ Rule ID | Category | Severity | Notes [HLQ007](https://github.com/NetFabric/NetFabric.Hyperlinq.Analyzer/blob/master/docs/reference/HLQ007_NonDisposableEnumerator.md) | Performance | Warning | Consider returning a non-disposable enumerator. [HLQ008](https://github.com/NetFabric/NetFabric.Hyperlinq.Analyzer/blob/master/docs/reference/HLQ008_ReadOnlyRefEnumerable.md) | Performance | Info | The enumerable is a value type. Consider making it `readonly`. [HLQ009](https://github.com/NetFabric/NetFabric.Hyperlinq.Analyzer/blob/master/docs/reference/HLQ009_RemoveOptionalMethods.md) | Performance | Info | Consider removing an empty optional enumerator method. -[HLQ010](https://github.com/NetFabric/NetFabric.Hyperlinq.Analyzer/blob/master/docs/reference/HLQ010_UseForLoop.md) | Performance | Warning | Consider using a `for` loop instead. [HLQ011](https://github.com/NetFabric/NetFabric.Hyperlinq.Analyzer/blob/master/docs/reference/HLQ011_ReadOnlyEnumeratorField.md) | Compiler | Error | Mutable value-type enumerators cannot be stored in a `readonly` field. [HLQ012](https://github.com/NetFabric/NetFabric.Hyperlinq.Analyzer/blob/master/docs/reference/HLQ012_UseCollectionsMarshalAsSpan.md) | Performance | Warning | Consider using `CollectionsMarshal.AsSpan()` when iterating a `List`. [HLQ013](https://github.com/NetFabric/NetFabric.Hyperlinq.Analyzer/blob/master/docs/reference/HLQ013_UseForEachLoop.md) | Performance | Warning | Consider using `foreach` when iterating an array or a `Span`. diff --git a/docs/reference/HLQ010_UseForLoop.md b/docs/reference/HLQ010_UseForLoop.md deleted file mode 100644 index fa04665..0000000 --- a/docs/reference/HLQ010_UseForLoop.md +++ /dev/null @@ -1,81 +0,0 @@ -# HLQ010: Use 'for' loop - -## Cause - -'foreach' loop is being used on a enumerable that has an indexer. - -## Severity - -Warning - -## Rule description - -When the collecton is an array, a 'foreach' loop is internally converted into a 'for'. This happens because the indexer of an array is a lot more efficient that the its enumerator. - -The indexer is usually more efficient but the compiler cannot make that assumption so, it doesn't make the conversion for other types. - -### Benchmarks - -Source: https://github.com/NetFabric/NetFabric.Hyperlinq.Analyzer/blob/master/NetFabric.Hyperlinq.Analyzer.Benchmarks/HLQ010_UseForLoop.cs - -``` - -BenchmarkDotNet v0.13.6, Windows 10 (10.0.19045.3269/22H2/2022Update) -Intel Core i7-7567U CPU 3.50GHz (Kaby Lake), 1 CPU, 4 logical and 2 physical cores -.NET SDK 8.0.100-preview.5.23303.2 - [Host] : .NET 6.0.20 (6.0.2023.32017), X64 RyuJIT AVX2 - .NET 6 : .NET 6.0.20 (6.0.2023.32017), X64 RyuJIT AVX2 - .NET 7 : .NET 7.0.8 (7.0.823.31807), X64 RyuJIT AVX2 - .NET 8 : .NET 8.0.0 (8.0.23.28008), X64 RyuJIT AVX2 - - -``` -| Method | Job | Runtime | Count | Mean | Error | StdDev | Median | Ratio | RatioSD | Code Size | Allocated | Alloc Ratio | -|-------- |------- |--------- |------ |-------------:|-----------:|-----------:|-------------:|-------------:|--------:|----------:|----------:|------------:| -| **Foreach** | **.NET 6** | **.NET 6.0** | **100** | **179.92 ns** | **13.386 ns** | **39.468 ns** | **161.90 ns** | **baseline** | **** | **99 B** | **-** | **NA** | -| For | .NET 6 | .NET 6.0 | 100 | 104.88 ns | 6.324 ns | 18.648 ns | 98.49 ns | 1.76x faster | 0.46x | 77 B | - | NA | -| | | | | | | | | | | | | | -| Foreach | .NET 7 | .NET 7.0 | 100 | 121.68 ns | 2.565 ns | 7.318 ns | 119.04 ns | baseline | | NA | - | NA | -| For | .NET 7 | .NET 7.0 | 100 | 61.44 ns | 0.571 ns | 0.446 ns | 61.32 ns | 2.03x faster | 0.14x | 73 B | - | NA | -| | | | | | | | | | | | | | -| Foreach | .NET 8 | .NET 8.0 | 100 | 71.27 ns | 1.464 ns | 2.991 ns | 69.95 ns | baseline | | NA | - | NA | -| For | .NET 8 | .NET 8.0 | 100 | 61.76 ns | 1.079 ns | 1.009 ns | 61.26 ns | 1.16x faster | 0.06x | 72 B | - | NA | -| | | | | | | | | | | | | | -| **Foreach** | **.NET 6** | **.NET 6.0** | **10000** | **13,881.17 ns** | **271.842 ns** | **302.151 ns** | **13,802.66 ns** | **baseline** | **** | **99 B** | **-** | **NA** | -| For | .NET 6 | .NET 6.0 | 10000 | 8,177.57 ns | 161.223 ns | 125.872 ns | 8,184.67 ns | 1.70x faster | 0.03x | 77 B | - | NA | -| | | | | | | | | | | | | | -| Foreach | .NET 7 | .NET 7.0 | 10000 | 10,779.54 ns | 81.614 ns | 63.719 ns | 10,784.76 ns | baseline | | NA | - | NA | -| For | .NET 7 | .NET 7.0 | 10000 | 5,701.41 ns | 109.829 ns | 102.734 ns | 5,664.83 ns | 1.89x faster | 0.04x | 73 B | - | NA | -| | | | | | | | | | | | | | -| Foreach | .NET 8 | .NET 8.0 | 10000 | 5,662.48 ns | 68.151 ns | 56.909 ns | 5,651.46 ns | baseline | | NA | - | NA | -| For | .NET 8 | .NET 8.0 | 10000 | 5,658.66 ns | 112.406 ns | 275.733 ns | 5,510.43 ns | 1.01x slower | 0.06x | 72 B | - | NA | - - -## How to fix violations - -Replace the 'foreach' loop for a 'for' loop. - -## When to suppress warnings - -When it's known that the indexer is not more efficient. - -## Example of a violation - -```csharp -var list = new List(new[] { 0, 1, 2, 3, 4, 5 }); - -foreach (var item in list) // using the enumerator - Console.WriteLine(item); -``` - -## Example of how to fix - -```csharp -var list = new List(new[] { 0, 1, 2, 3, 4, 5 }); - -for (var index = 0; index < list.Count; index++) -{ - var item = list[index]; // using the indexer - Console.WriteLine(item); -} -```