## 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:

- IList
- IList<T>
- IReadonlyList<T>

### 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

    int GetAt(List<int> data, int index)
        => data.ElementAt(index);

    int GetFirst(List<int> data)
        => data.First();

    int GetLast(List<int> data)
        => data.Last();

#### Compliant solution

    int GetAt(List<int> data, int index)
        => data[index];

    int GetFirst(List<int> data)
        => data[0];

    int GetLast(List<int> data)
        => data[data.Count-1];

## Resources

### Documentation

- IList.Item[Int32]
- IList<T>.Item[Int32]
- IReadonlyList<T>.Item[Int32]

### Benchmarks

Method | Runtime | Mean | Standard Deviation
--- | --- | --- | ---
ElementAt | 3,403.4 ns | 28.52 ns | 26.67 ns
Index | 478.0 ns | 6.93 ns | 6.48 ns
First | 6,160.0 ns | 57.66 ns | 53.93 ns
First_Index | 485.7 ns | 5.81 ns | 5.15 ns
Last | 6,034.3 ns | 20.34 ns | 16.98 ns
Last_Index | 408.3 ns | 2.54 ns | 2.38 ns

#### Glossary

- Mean
- Standard Deviation

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 int ElementAt()
    {
        int result = default;

        for (var i = 0; i < LoopSize; i++)
        {
            result = data.ElementAt(i);
        }

        return result;
    }

    [Benchmark]
    public int Index()
    {
        int result = default;

        for (var i = 0; i < LoopSize; i++)
        {
            result = data[i];
        }

        return result;
    }

    [Benchmark]
    public int First()
    {
        int result = default;

        for (var i = 0; i < LoopSize; i++)
        {
            result = data.First();
        }

        return result;
    }

    [Benchmark]
    public int First_Index()
    {
        int result = default;

        for (var i = 0; i < LoopSize; i++)
        {
            result = data[0];
        }

        return result;
    }

    [Benchmark]
    public int Last()
    {
        int result = default;

        for (var i = 0; i < LoopSize; i++)
        {
            result = data.Last();
        }

        return result;
    }

    [Benchmark]
    public int Last_Index()
    {
        int result = default;

        for (var i = 0; i < LoopSize; i++)
        {
            result = data[data.Count - 1];
        }

        return result;
    }

Hardware configuration:

    BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.4412/22H2/2022Update)
    11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores
    .NET SDK=8.0.301
      [Host]   : .NET 8.0.6 (8.0.624.26715), X64 RyuJIT AVX2
      .NET 8.0 : .NET 8.0.6 (8.0.624.26715), X64 RyuJIT AVX2