Confused about event supcription of functions with int parameters

I am building a game, in which you control a character, which has different abilities. The abilities are activated by hotkeys (on a standard keyboard). To set this up im using unitys new inputsystem with a generacted c-class.

For the sake of clarity of the below code i have to mention, that my generated c-class is named PlayerInput.
Some variable names use spell instead of ability. Unfortunately i did not keep this 100% consistent.

Below is the code, which i use to setup the hotkeys. I have reduced my orginal code to the part relevant to this question.

public class PlayerCasting : MonoBehaviour {
    private PlayerInput controls;
    public PlayerAbility[] abilityList; // PlayerAbility is a class i have written. For the purpose of this Question it should be enough to know, that this array contains the useable abilities

    private void Awake() {
        controls = new PlayerInput();
    }

    private void OnEnable() {
        controls.spellkeys.Enable(); // spellkeys is the name of the used ActionMap
    }

    private void OnDisable() {
        controls.spellkeys.Disable();
    }

    void Start() {
        SetUpSpellEvents();
    }

    void SetUpSpellEvents() {
        InputActionMap spellKeys = ((InputActionMap) controls.spellkeys);

        // int[] dummy = new int[spellKeys.actions.Count];
        // for (int i = 0; i < dummy.Length; i++)
        //     dummy[i] = i;
        // foreach (int i in dummy) {
        //     if (i >= abilityList.Length || abilityList[i] == null) continue;
        //     spellKeys.actions[i].started += _ => ActivateAbility(i);
        // }

        foreach (int i in Enumerable.Range(0, spellKeys.actions.Count+1)) {
            if (i >= abilityList.Length || abilityList[i] == null) continue;
            spellKeys.actions[i].started += _ => ActivateAbility(i);
        }

        // for (int i = 0; i < spellKeys.actions.Count; i++) {
        //     if (i >= abilityList.Length || abilityList[i] == null)
        //         continue;
        //     spellKeys.actions[i].started += _ => ActivateAbility(i);
        // }

        // int i = -1;
        // foreach (InputAction inputAction in spellKeys) {
        //     i++;
        //     if (i >= abilityList.Length || abilityList[i] == null)
        //         continue;
        //     inputAction.started += _ => ActivateAbility(i);
        // }
    }

    void ActivateAbility(int abilityPosition) {
        PlayerAbility ability = abilityList[abilityPosition];
        // Do something with ability
    }

}

You may note, that the SetUpSpellEvents-function has 4 variants of the for- / foreach-loop setting up the event-listeners. 3 are comment out and 1 is currently used. All 4 variants should to the same thing (in my opinion).

The first 2 variants of the loop work, while the last 2 do not work.
By not working i mean, that all InputActions-started-events call the function ActivateAbility() with the same value for the int-Parameter abilityPosition (e.g. spellKeys.actions.Count / spellKeys.actions.Count-1). In both cases this should be the final value for i.

To be even more precise:
Pressing a key for a InputAction in the used ActionMap (which consists of 10 InputActions) results in:

with either of the first 2 variants:
Pressing the key for the first InputAction calls the function ActivateAbility(0)
Pressing the key for the second InputAction calls the function ActivateAbility(1)

Pressing the key for the last InputAction calls the function ActivateAbility(9)

if im using the third variant
Pressing the key for the first InputAction calls the function ActivateAbility(10)

Pressing the key for the last InputAction calls the function ActivateAbility(10)

if im using the fourth variant
Pressing the key for the first InputAction calls the function ActivateAbility(9)

Pressing the key for the last InputAction calls the function ActivateAbility(9)

My Question is: Why exactly do the last 2 loop-variants not work. Why is ActivateAbility() always called with the same (final?) value of i?

I think you’re experiencing lamba variable capture: Lambda expressions - Lambda expressions and anonymous functions - C# reference | Microsoft Learn

If you have a hotkey system, you’ll need further encapsulation. Have an object that wraps around a hotkey. They can store their index, and you can pass your input action map for them to register themselves to the input map.

Then you can register individual abilities to your hotkey objects.