Oct 26, 2013

Closures in C# - Answer


In the previous post I've asked what would be the output of a simple C# application:

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
   private static void Main(string[] args)
   {
      foreach (var func in GetFuncs1())
      {
         Console.WriteLine("[first] {0}", func());
      }

      foreach (var func in GetFuncs2())
      {
         Console.WriteLine("[second] {0}", func());
      }
   }

   private static List<Func<int>> GetFuncs1()
   {
      var fs = new List<Func<int>>();

      for (int i = 0; i < 3; i++)
      {
         fs.Add(() => i);
      }
      return fs;
   }

   private static List<Func<int>> GetFuncs2()
   {
      var fs = new List<Func<int>>();

      foreach (var i in Enumerable.Range(0, 3))
      {
         fs.Add(() => i);
      }
      return fs;
    }  
}
The correct answer is: it depends!

In order to understand the issue you need to undertand Closures and captured variables in C# (for a deeper understanding about these subjects I recomed reading here and here).

Basically in both methods (GetFuncs1 and 2) we are creating lambda expressions that references a local variable (i); since the CLR garantees that the memory allocated for such variable will be reclaimed as soon as it is not referenced, i.e, long before the usage of the lambda expression, the C# compiler "captures" the variable (if you are curious about how it does it, open the assembly using ILDASM or ILSpy to inspect the IL code, I do recommend) and here start our issues.

As I said the compiler captures the variable, not its value; so, calling GetFuncs1() produces code that is equivalent to something like (see the highlighted lines 20, 25-28 and 29) (the actual code is quite different but for our purposes the following code can be seen as equivalent):

using System;
using System.Collections.Generic;

class Program
{
 private static void Main(string[] args)
 {
  foreach (var func in GetFuncs1())
  {
   Console.WriteLine("[first] {0}", func());
  }
 }

 private static List<Func<int>> GetFuncs1()
 {
  var fs = new List<Func<int>>();

  for (ret = 0; ret < 3; ret++)
  {
   fs.Add(ret_func);
  }
  return fs;
 }

 private static int ret_func()
 {
  return ret;
 }

 private static int ret;
}
That means that we are always calling ret_func() and this function will return the value of ret which is updated in the for loop!

Ok, so now you understand why the output was 3,3,3 but two questions come up (at least):

  1. Is there a way to avoid this behavior so the output would be 0, 1 and 2 ?
  2. Why the hell I said that the output of the program "depends" ? And more importantly, depends on what?
The answer for the first question is yes; simply declare a new variable inside the for loop and assign "i" to it (line 20), then return the value of this new variable from within the lambda expression (line 21) :
using System;
using System.Collections.Generic;

class Program
{
 private static void Main(string[] args)
 {
  foreach (var func in GetFuncs1())
  {
   Console.WriteLine("[first] {0}", func());
  }
 }

 private static List<Func<int>> GetFuncs1()
 {
  var fs = new List<Func<int>>();

  for (int i = 0; i < 3; i++)
  {
   var capture = i;
   fs.Add(() => capture);
  }
  return fs;
 }
}
Running the application now produces the expected result. What changed is that now, since we are creating a new variable in every iteration, the compiler will emit code equivalent to:
using System;
using System.Collections.Generic;

class Program
{
 private static void Main(string[] args)
 {
  foreach (var func in GetFuncs1())
  {
   Console.WriteLine("[first] {0}", func());
  }
 }

 private static List<Func<int>> GetFuncs1()
 {
  var fs = new List<Func<int>>();

  for (int i = 0; i < 3; i++)
  {
   var h = new Holder {value = i};
   fs.Add(h.GetIt);
  }
  return fs;
 }

 class Holder
 {
  public int value;

  public int GetIt()
  {
   return value;
  }
 }
}
Again, now it became pretty easy to understand why we get the expected output; in each iteration the compiler is simply instantiating a new object to hold the value of "i" at that point in time.

Ok, so from now on, everytime you create lambda expressions / anonymous functions that capture local variables I am sure you'll pay attention and make sure you get the behaviour you want.

Regarding the second point, i.e, the "depends" part, if this blog happens to have more than a few readers I am sure that, when running the program, some of you got "unexpected" different outputs twice (i.e, 3,3,3 and 2,2,2) while some of you got 3,3,3 and 0,1,2!

The difference? The version of the C# compiler used!

See the picture below:

It happens that in C# 5 the behavior for captured variables of a foreach loop has changed! In this case the compiler will emit code as if in each iteration a new local variable were allocated (the same trick I showed you before); if you followed me you now understand why when compiling with VS 2012 the code produced the expected result.

Now the 1 million dollars question: why on earth the version that uses a simple for has not been updated with the same behavior? Well, I don't have (and I don't pretend to have) the definitive answer. What I can say is that IMHO it is at least "confusing". New developers (which are most likely unaware of this behavior) will, invariably, be caught by this at least once (but it can be twice)!

If these new developers happen to first use a for loop (always with lambdas / anonymous methods capturing local variables) they may find themselves wondering why their lambdas/anonymous methods are observing the updated value of a captured variable and then, when they finally realize what's going on, they will tend to use the "declare a local variable in each iteration" trick everywhere (including in foreach loops). By the other hand if these developers happen to first use a foreach loop then they will most likely find themselves wondering why they observe different behaviors when using lambdas/anonymous methods!

Even though I understand the reasoning in this discussion I don't fully agree - parts of my brain still thinks this simply introduces inconsitencies - but this is just my opnion.

If you are interested in reading about this topic in the C# language specification, it can be found in chapter 8.8.4.

What do you think?

Closures em C# - Resposta


No post anterior eu deixei como pergunta qual seria a saída de um programa simples em C#:

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
   private static void Main(string[] args)
   {
      foreach (var func in GetFuncs1())
      {
         Console.WriteLine("[first] {0}", func());
      }

      foreach (var func in GetFuncs2())
      {
         Console.WriteLine("[second] {0}", func());
      }
   }

   private static List<Func<int>> GetFuncs1()
   {
      var fs = new List<Func<int>>();

      for (int i = 0; i < 3; i++)
      {
         fs.Add(() => i);
      }
      return fs;
   }

   private static List<Func<int>> GetFuncs2()
   {
      var fs = new List<Func<int>>();

      foreach (var i in Enumerable.Range(0, 3))
      {
         fs.Add(() => i);
      }
      return fs;
    }  
}

A resposta para a pergunta é: depende.

(antes de continuar quero ressaltar que toda vez que me referir a um laço for/foreach, assuma que o mesmo se encontra no contexto de lambda expressions / métodos anônimos que capturam variáveis locais / parâmetros)

Para que você possa enterder o problema melhor é necessário antes entender um pouco sobre closures e a sua relação com o uso de variáveis (locais / parâmetros). Se você precisar refrescar a memória recomendo a leitura deste blog post e também deste outro).

Basicamente ambos os métodos do exemplo (GetFuncs1 e 2) criam lambda expressions as quais em seus corpos referenciam a variável local (i)(linhas 26 e 37); para evitar que a área de memória reservada para tal variável seja reutilizada pela CLR o compilador C# "captura" a variável (você pode utilizar o ILDASM ou ILSpy para verificar o código IL gerado e assim entender melhor este processo) e é aqui que os nossos "problemas" começam.

Observe que eu disse que o compilador "captura" as variáveis, não seus valores no momento da criação das lambda expressions; assim, supondo que tivessemos apenas a chamada ao método GetFuncs1(), o código resultante seria equivalente ao código abaixo (note as linhas marcadas: 20, 25-28 e 30) (o código gerado na realidade é bem diferente mas para nossos propósitos podemos tratá-lo como equivalente):

using System;
using System.Collections.Generic;

class Program
{
 private static void Main(string[] args)
 {
  foreach (var func in GetFuncs1())
  {
   Console.WriteLine("[first] {0}", func());
  }
 }

 private static List<Func<int>> GetFuncs1()
 {
  var fs = new List<Func<int>>();

  for (ret = 0; ret < 3; ret++)
  {
   fs.Add(ret_func);
  }
  return fs;
 }

 private static int ret_func()
 {
  return ret;
 }

 private static int ret;
}
Como vemos, ao invés de criar um método para representar o corpo das lambda expressions em cada iteração do laço, o compilador emitiu um único método estático (ret_func() inserindo referências para o mesmo na lista de funções) que simplesmente retorna o valor do campo ret o qual, por sua vez, é atualizado no laço for!

Agora que você já entende porque a saída do programa foi 3,3,3 restam, pelo menos, duas questões:

  1. Como obter o comportamento desejado, ou seja, que a saída do programa seja 0, 1 e 2 ?
  2. Porque eu disse que a saída do programa "dependia" de algo, e o mais importante, depende do que?
A resposta para a primeira pergunta é sim; (veja o programa abaixo) basta declarar uma variável local dentro do laço for e atribuir a variável "i" para a mesma (linha 20) e, então, retornar o valor desta variável no corpo da lambda expression (linha 21) :
using System;
using System.Collections.Generic;

class Program
{
 private static void Main(string[] args)
 {
  foreach (var func in GetFuncs1())
  {
   Console.WriteLine("[first] {0}", func());
  }
 }

 private static List<Func<int>> GetFuncs1()
 {
  var fs = new List<Func<int>>();

  for (int i = 0; i < 3; i++)
  {
   var capture = i;
   fs.Add(() => capture);
  }
  return fs;
 }
}
Se você executar o programa agora verá que a saída do mesmo é exatamente a esperada; isto ocorre porque, agora, como declaramos uma variável local dentro do laço o compilador irá emitir código equivalente ao código abaixo (efetivamente capturando o valor da variável no momento em que a lambada expression é criada):
using System;
using System.Collections.Generic;

class Program
{
 private static void Main(string[] args)
 {
  foreach (var func in GetFuncs1())
  {
   Console.WriteLine("[first] {0}", func());
  }
 }

 private static List<Func<int>> GetFuncs1()
 {
  var fs = new List<Func<int>>();

  for (int i = 0; i < 3; i++)
  {
   var h = new Holder {value = i};
   fs.Add(h.GetIt);
  }
  return fs;
 }

 class Holder
 {
  public int value;

  public int GetIt()
  {
   return value;
  }
 }
}
Novamente ficou mais simples entender porque este código produz o resultado esperado: em cada iteração do laço o compilador instanciou um objeto para armazenar o valor de "i" naquele momento.

Estou certo de que, de agora em diante, toda vez que você utilizar lambda expressions / métodos anônimos você irá certificar-se de usar a construção compatível com o resultado desejado.

Quanto à segunda questão, se este blog tiver um número razoável de leitores, teremos dois grupos com resultados diferentes: 3,3,3 / 2,2,2 e 3,3,3 / 0,1,2 (que é a saída esperada)!

A diferença nos resultados esta relacionada à versão do compilador C# utilizado.

Veja a imagem abaixo:

A versão 5 do C# mudou o comportamento de variáveis capturadas em laços foreach! A partir desta versão o compilador emite código como se a cada iteração uma nova variável local fosse alocada (o mesmo truque que eu apresentei acima);

Agora eu me questiono porque o comportamento de laços for não foram modificados também. O que posso dizer é que, na minha opinião, esta decisão só introduz confusão. Desenvolvedores iniciantes na linguagem irão, invariavelmente, ser surpreendidos por esta diferença de comportamento.

Caso estes desenvolvedores tenham contato primeiramente com laços for os mesmos ficaram tentando entender porque suas lambdas expressions/métodos anônimos estão observando uma valor atualizado da variável capturada e, então, quando eles entenderem o que esta ocorrendo tenderão a usar o truque de "declarar uma variável local dentro do laço" (inclusive em laços foreach). Por outro lado, caso estes desenvolvedores tenham contato com o laço foreach primeiro, eles correm o risco de em algum momento usar um laço for e introduzir bugs (uma vez que os mesmos assumirão que o valor da variável do laço será capturada).

Há uma discussão sobre o assunto que, ainda que eu compreenda os argumentos, não concordo completamente com os mesmos - partes do meu cérebro ainda acham que esta diferença no comportamento é uma inconsistência gratuíta - mas esta é apenas minha opinião.

Se você estiver interessado em mais detalhes sobre o assunto recomendo a leitura do capítulo 8.8.4 da especificação da linguagem C#.

Finalmente um alerta: se você for portar projetos de uma versão do C# anterior à 5.0 para a versão 5.0 ou mais nova preste atenção na combinação lambda expressions / laços for pois como vimos é possível ocorrer mudanças de comportamento.

O que você acha?

(read this post in english)

Oct 5, 2013

Closures in C#


Hi

Given the following code:

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
   private static void Main(string[] args)
   {
      foreach (var func in GetFuncs1())
      {
         Console.WriteLine("[first] {0}", func());
      }

      foreach (var func in GetFuncs2())
      {
         Console.WriteLine("[second] {0}", func());
      }
   }

   private static List<Func<int>> GetFuncs1()
   {
      var fs = new List<Func<int>>();

      for (int i = 0; i < 3; i++)
      {
         fs.Add(() => i);
      }
      return fs;
   }

   private static List<Func<int>> GetFuncs2()
   {
      var fs = new List<Func<int>>();

      foreach (var i in Enumerable.Range(0, 3))
      {
         fs.Add(() => i);
      }
      return fs;
    }  
}

What do you expect as the output? (try answering before running it)

Does it produces the results you expected? No? Do you understand why?

(If you are using Resharper it will warn you that you wrote code that captures variables and can produce "unexpected" results)

In the next post I'll give the answer and discuss it.

Happy coding.

(leia este post em Português)

Closures em C#


Olá.

Dado o seguinte programa em C# o que você espera que o mesmo imprima? (tente responder a pergunta sem compilar/executar o mesmo):

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
   private static void Main(string[] args)
   {
      foreach (var func in GetFuncs1())
      {
         Console.WriteLine("[first] {0}", func());
      }

      foreach (var func in GetFuncs2())
      {
         Console.WriteLine("[second] {0}", func());
      }
   }

   private static List<Func<int>> GetFuncs1()
   {
      var fs = new List<Func<int>>();

      for (int i = 0; i < 3; i++)
      {
         fs.Add(() => i);
      }
      return fs;
   }

   private static List<Func<int>> GetFuncs2()
   {
      var fs = new List<Func<int>>();

      foreach (var i in Enumerable.Range(0, 3))
      {
         fs.Add(() => i);
      }
      return fs;
    }  
}


Se você rodar o mesmo, o resultado obtido é compatível com sua expectativa? Não? Você entende o porque?

No próximo post vou discutir o comportamento deste programa e também como evitar algumas "armadilhas" relacionadas a Closures em C#.

(read this post in english)