Closures and Pass by Reference
What do you think the following code will do?
- Compile time error
- Run time error
- Work fine
1: static void Main(string[] args)
2: {
3: int x = 10;
4: int y = 5;
5: Swap(ref x, ref y);
6: }
7:
8:
9: static void Swap(ref int x, ref int y)
10: {
11: int temp = x;
12: x = y;
13: y = temp;
14: Func<int> closure = () => x;
15: }
If you said 1. Compile time error then you would be correct. The resulting error message is:
Cannot use ref or out parameter 'x' inside an anonymous method, lambda expression, or query expression
The reason for this is that although C# provides the ability to pass parameters by reference (as opposed to by value), it offers no way to assign to a variable by reference.
This ability exists in C++ :
1: int x = 5;
2: int & y = x;
Now y refers to the same variable location as x.
So, what does this all have to do with the original code? As I described in my previous post Understanding Variable Capturing in C#, since we are using the variable x in a lambda expression (a.k.a. a closure) the C# compiler will rewrite the method so that it looks something like this:
1: class Capture
2: {
3: public int x;
4: public int Lambda()
5: {
6: return this.x;
7: }
8: }
9:
10: static void Swap(ref int x, ref int y)
11: {
12: Capture capture = new Capture();
13: capture.x = x;
14: int temp = capture.x;
15: capture.x = y;
16: y = temp;
17: Func<int> closure = () => capture.x;
18: }
Do you see the problem now? On line 13 the ref parameter x is being assigned to the member variable x of the Capture class. But since you can't assign by reference this would change the semantics of the method. The C# compiler is smart enough to know this, so it throws the error. It won't generate code that has different meaning than what the user intended.