Mais conhecido por seu uso em Go e Swift, a proposta C# 1398 procura adicionar declarações de diferimento (defer) . Caso não esteja estiver familiarizado com o conceito, ele pode ser resumido como um bloco final que aparece no início de algum código em vez do final.
Aqui está um exemplo da proposta:
{
SomeType thing = Whatever...;
defer {
thing.Free();
}
// some code code using thing
}
Isso seria interpretado como:
{
SomeType thing = Whatever...;
try
{
// some code code using thing
}
finally
{
thing.Free();
}
}
Como isso se parece muito com um bloco final, muitos desenvolvedores acham que a sintaxe é redundante. Neal Gafter, da Microsoft, apresenta um contra-argumento afirmando que tem suas vantagens:
- Eles não exigem a implementação de alguma interface, como IDisposable, ou a criação de objetos auxiliares.
- Eles não adicionam um nível de recuo ao código, o que seria desajeitado quando há mais de um.
- Eles não movem o código de limpeza para o final, como try-finally, mas mantêm o código de limpeza logicamente com a declaração e a inicialização da coisa que está sendo limpa. Dessa forma, eles facilitam o entendimento do código.
Alireza Habibi rejeita isso com:
Eu não acho que o problema real do uso da instrução seja de recuos adicionais nem que você tenha que implementar uma interface - isso não é uma coisa ruim, então existe um contrato para tipos que precisam de limpeza. Eu acho que o problema real aqui é que você pode esquecer de desprezar tipos descartáveis; adiar declaração não ajuda com isso, em vez disso, incentiva você a não implementar a interface IDisposable e explicitamente chamar métodos como Close ou Free, que na presença de IDisposable parecem códigos cheiros.
Sam também questiona a nova palavra-chave:
Se o caso de uso para defer é a limpeza de recursos, então parece ser um anti-pattern para eu não exigir que a coisa implemente IDisposable. A implementação de IDisposable permite que as ferramentas avisem se você criar uma variável descartável como local e nunca descartá-la. Se você aceitar os recursos para não exigir IDisposable, você perderá esse benefício.
Outra queixa é que dificulta a compreensão da ordem de operações. Um usuário que utiliza o identificador HaloFour oferece este exemplo:
static void Main() {
Console.WriteLine("Hello"); //1
defer {
Console.WriteLine("Foo"); //5
}
defer {
Console.WriteLine("Bar"); //4
}
defer {
Console.WriteLine("Baz"); //3
}
Console.WriteLine("World"); //2
}
Para sua conveniência, a ordem em que as linhas serão executadas foram adicionadas como comentários. Como se pode ver, o código é executado de cima para baixo, pulando algumas linhas e, em seguida, de baixo para cima.
Exception Handling
Uma questão discutida é qual será o efeito de uma exceção em um bloco de defer. Com um try-finally normal, o bloco finally atual é abortado, mas outros blocos finally o envolvem ainda serão executados. Seria esse o caso para adiar ou a primeira falha faria com que os outros fossem contornados? O consenso geral parece ser que os blocos restantes de defer ainda serão executados.
O Swift evita esse problema ao não permitir código que possa lançar uma exceção a ser chamada de dentro de um bloco de adiamento.
No momento, a proposta da instrução defer está marcada como candidata C# 8.x, mas isso não significa que ela foi escolhida para fazer parte de uma versão futura do C#. Seu status oficial é "campeão de propostas", o que significa que um membro da equipe de C# está interessado em representar o recurso em uma futura reunião de design da linguagem (LDM).