After running this loop, when the action of one of my objects is invoked, the effect is as if the loop had used the last pair.Key in the dictionary for all the lambda expressions.
Yes, that's exactly what's happening. You have a single variable pair
that is used by all the event handlers. After the loop the variable contains the key of the last item in the loop.
By creating a local variable in the scope and using that in the lambda expression, you are actually creating a closure for each event handler. The local variable is not stored on the stack as a regular local varible, but in the closure, and as each event handler has its own closure, you get one version of the local variable for each event handler.
(There is a closure with the first code also, but that is just for the variable to survive the current scope, the event handlers share the same closure.)