Why is this an issue?
Indexes in C# provide direct access to an element at a specific position within an array or collection. When compared to Enumerable
methods, indexing can be more efficient for certain scenarios, such as iterating over a large collection, due to avoiding the overhead of checking the
underlying collection type before accessing it.
This applies to types that implement one of these interfaces:
What is the potential impact?
We measured a significant improvement in execution time. For more details see the Benchmarks
section from the More info
tab.
How to fix it
If the type you are using implements IList
, IList<T>
or IReadonlyList<T>
, it implements
this[int index]
. This means calls to First
, Last
, or ElementAt(index)
can be replaced with
indexing at 0
, Count-1
and index
respectively.
Code examples
Noncompliant code example
Function GetAt(data As List(Of Integer), index As Integer) As Integer
Return data.ElementAt(index)
End Function
Function GetFirst(data As List(Of Integer)) As Integer
Return data.First()
End Function
Function GetLast(data As List(Of Integer)) As Integer
Return data.Last()
End Function
Compliant solution
Function GetAt(data As List(Of Integer), index As Integer) As Integer
Return data(index)
End Function
Function GetFirst(data As List(Of Integer)) As Integer
Return data(0)
End Function
Function GetLast(data As List(Of Integer)) As Integer
Return data(data.Count-1)
End Function
Resources
Documentation
Benchmarks
Method |
Runtime |
Mean |
StdDev |
ElementAt |
.NET 7.0 |
15,193.1 ns |
233.47 ns |
Index |
.NET 7.0 |
9,465.6 ns |
148.16 ns |
First |
.NET 7.0 |
7,790.2 ns |
165.70 ns |
First_Index |
.NET 7.0 |
398.5 ns |
5.36 ns |
Last |
.NET 7.0 |
7,398.2 ns |
152.48 ns |
Last_Index |
.NET 7.0 |
347.3 ns |
5.47 ns |
ElementAt |
.NET Framework 4.6.2 |
12,205.7 ns |
298.49 ns |
Index |
.NET Framework 4.6.2 |
8,917.8 ns |
51.55 ns |
First |
.NET Framework 4.6.2 |
5,109.1 ns |
100.13 ns |
First_Index |
.NET Framework 4.6.2 |
566.0 ns |
6.56 ns |
Last |
.NET Framework 4.6.2 |
5,052.7 ns |
76.02 ns |
Last_Index |
.NET Framework 4.6.2 |
680.7 ns |
9.56 ns |
The results were generated by running the following snippet with BenchmarkDotNet:
private List<byte> data;
private Random random;
[Params(1_000_000)]
public int SampleSize;
[Params(1_000)]
public int LoopSize;
[GlobalSetup]
public void Setup()
{
random = new Random(42);
var bytes = new byte[SampleSize];
random.NextBytes(bytes);
data = bytes.ToList();
}
[Benchmark]
public void ElementAt()
{
for (int i = 0; i < LoopSize; i++)
{
var index = random.Next(0, SampleSize);
_ = data.ElementAt(index);
}
}
[Benchmark]
public void Index()
{
for (int i = 0; i < LoopSize; i++)
{
var index = random.Next(0, SampleSize);
_ = data[index];
}
}
[Benchmark]
public void First()
{
for (int i = 0; i < LoopSize; i++)
{
_ = data.First();
}
}
[Benchmark]
public void First_Index()
{
for (int i = 0; i < LoopSize; i++)
{
_ = data[0];
}
}
[Benchmark]
public void Last()
{
for (int i = 0; i < LoopSize; i++)
{
_ = data.Last();
}
}
[Benchmark]
public void Last_Index()
{
for (int i = 0; i < LoopSize; i++)
{
_ = data[data.Count - 1];
}
}
Hardware configuration:
BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.2846/22H2/2022Update)
11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 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.1 (4.8.9139.0), X64 RyuJIT VectorSize=256