Eu estava respondendo a uma pergunta sobre a possibilidade de fechamentos (legitimamente) estenderem a vida útil do objeto quando deparei com um código-gen extremamente curioso da parte do compilador C # (4.0, se necessário).
A reprodução mais curta que posso encontrar é a seguinte:
- Crie um lambda que captura um local ao chamar um método estático do tipo que o contém.
- Atribua a referência de delegação gerada a um campo de instância do objeto que contém.
Resultado: o compilador cria um objeto de fechamento que faz referência ao objeto que criou o lambda, quando não há motivo para - o destino 'interno' do delegado é um método estático e os membros da instância do objeto de criação de lambda não precisam ser (e não é) tocado quando o delegado é executado. Efetivamente, o compilador está agindo como o programador capturou this
sem motivo.
class Foo
{
private Action _field;
public void InstanceMethod()
{
var capturedVariable = Math.Pow(42, 1);
_field = () => StaticMethod(capturedVariable);
}
private static void StaticMethod(double arg) { }
}
O código gerado a partir de uma versão de compilação (descompilado para C # 'mais simples') se parece com o seguinte:
public void InstanceMethod()
{
<>c__DisplayClass1 CS$<>8__locals2 = new <>c__DisplayClass1();
CS$<>8__locals2.<>4__this = this; // What's this doing here?
CS$<>8__locals2.capturedVariable = Math.Pow(42.0, 1.0);
this._field = new Action(CS$<>8__locals2.<InstanceMethod>b__0);
}
[CompilerGenerated]
private sealed class <>c__DisplayClass1
{
// Fields
public Foo <>4__this; // Never read, only written to.
public double capturedVariable;
// Methods
public void <InstanceMethod>b__0()
{
Foo.StaticMethod(this.capturedVariable);
}
}
Observe que o <>4__this
campo do objeto de fechamento é preenchido com uma referência de objeto, mas nunca é lido (não há motivo).
Então, o que está acontecendo aqui? A especificação de idioma permite isso? Isso é um bug / estranheza do compilador ou existe um bom motivo (que estou claramente ausente) para o fechamento fazer referência ao objeto? Isso me deixa ansioso porque isso parece uma receita para programadores felizes em encerrar (como eu) introduzir involuntariamente vazamentos de memória estranhos (imagine se o delegado foi usado como manipulador de eventos) nos programas.
this
.Respostas:
Isso com certeza parece um bug. Obrigado por chamar minha atenção. Eu vou dar uma olhada. É possível que ele já tenha sido encontrado e corrigido.
fonte
Parece ser um bug ou desnecessário:
Eu executo você exemplo em IL lang:
Exemplo 2:
in cl: (Note !! Agora a referência se foi!)
Exemplo 3:
em IL: (este ponteiro está de volta)
E nos três casos, o método-b__0 () - tem a mesma aparência:
E nos três casos, há uma referência a um método estático, tornando-o mais estranho. Então, depois dessa análise, vou dizer que é um bug / não adianta. !
fonte
Foo.InstanceMethod
é feito estático, isso removeria a referência também? Eu ficaria grato por saber.Foo.InstanceMethod
também fosse estático, não haveria instância à vista e, portanto, não havia comothis
capturar o fechamento.