Closures and Pass by Reference

What do you think the following code will do?

  1. Compile time error
  2. Run time error
  3. 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.