A próxima proposta da série de artigos sobre o futuro do C# aborda a possibilidade de novas construções de propriedades de extensão. Esta é uma funcionalidade muito desejada, mas é senso comum que sua implementação pode não ser possível, pois ela introduziria vazamentos de memória.
Na verdade, todos os recursos necessários para criação das construções de propriedades de extensão existem desde o .NET 4.0. O segredo é que se pode usar a classe ConditionalWeakTable para armazenar o mapeamento entre objetos e campos de extensão. De fato, a classe ConditionalWeakTable foi criada especificamente para oferecer esta capacidade à linguagens de tipagem dinâmica.
Sam Harwell demonstrou como utilizar corretamente esta classe:
private static readonly ConditionalWeakTable> _extensionField_f; public static FieldType get_f(T obj) { StrongBox box; if (!_extensionField_f.TryGetValue(obj, out box)) return default(FieldType); return box.Value; } public static void set_f(T obj, FieldType value) { StrongBox box = _extensionField_f.GetOrCreateValue(obj); box.Value = value; }
Classes de Extensão
O exemplo anterior demonstra uma implementação, mas pode-se ainda considerar uma questão de sintaxe. E neste ponto é que entra um conceito chamado "classe de extensão". Uma classe de extensão poderia ser definida com a seguinte sintaxe:
public extension class MyClassExtensions : MyClass
Qualquer método ou propriedade inserido na classe MyClassExtensions seria automaticamente tratado como um método ou propriedade de extensão da classe MyClass. Visualmente, os membros da classe MyClassExtensions seriam iguais aos membros declarados diretamente na classe MyClass. A sintaxe antiga na qual explicitamente marcava um método como estático e utilizava a palavra reservada "this" não seria mais necessária.
Campos e eventos declarados dentro de uma classe de extensão podem ser implementados por meio da já mencionada classe ConditionalWeakTable. As propriedades podem ser explícitas ou automáticas. Propriedades automáticas também utilizariam a classe ConditionalWeakTable como mecanismo de armazenamento.
Extensões Estáticas
Um outro caso de uso passível de aplicação do conceito de classes de extensão é algo informalmente chamado de "membros de extensão estáticos". Isto permitiria adicionar métodos estáticos a uma classe da mesma maneira que se adiciona métodos à objetos.
Para clarificar a utilização deste conceito, Erik Schierboom escreveu:
Isto permitiria estender a classe Assert no xUnit, por exemplo.
Considerações
Ainda que comum em linguagens de tipagem dinâmica, a possibilidade de adicionar estado arbitrariamente a uma classe parece algo desconcertante para alguns desenvolvedores. HellBrick resumiu suas preocupações sobre campos de extensão:
Pode até parecer natural tentar esta abordagem, mas tenho receio que esta funcionalidade possa ser facilmente abusada. Se quiser adicionar estado a uma instância de uma classe, creio que na maioria dos casos seria muito melhor derivar o estado da própria classe ou criar um envelope ao redor dela. Nos dois casos, suas intenções ficariam mais explícitas. Utilizar algumas referências fracas "automágicas" e que não possuem um tempo de vida claro como uma abordagem alternativa para projetar classes talvez tenha algum apelo como uma solução barata e produtiva mas que, ao mesmo tempo, apresenta potencial de gerar um grande volume de código confuso e que será difícil de manter.
O contra argumento é que sem campos de extensão não seria possível oferecer auto-propriedades por meio de extensão. Segundo Christaut:
Mesmo que isto seja desencorajado, se for permitido utilizar setters de propriedades, os desenvolvedores vão tentar criar suas próprias soluções e metade deles vai errar de maneira sutil. Por exemplo, talvez eles criem uma instância de Dictionary<MyClass, String> e armazenem o valor nesta instância. Neste caso, o valor armazenado nunca será processado pelo coletor de lixo (garbage collector) e o desenvolvedor acabou de criar um vazamento de memória. A maioria dos desenvolvedores sequer conhecem o conceito de referências fracas ou a classe ConditionalWeakTable.
Então, se realmente desejamos desencorajar a adição de estado adicional (o que significa abdicar dos campos de extensão), creio que a única forma de fazê-lo é explicitamente permitir apenas getters de propriedades e não suportar setters ou mesmo getters exclusivos de auto-propriedades.
Isto seria razoável. Mas meu palpite é que as pessoas vão ter dificuldades com esta limitação. Não tenho certeza se somente getters cobririam todos os cenários desejados. Ou se devíamos dizer que ele cobre cenários suficientes e o restante não vale a pena ser considerado.
Um outro problema com os campos de extensão é o armazenamento de objetos descartáveis (disposable). Ao armazenar um objeto descartável em uma instância da classe ConditionalWeakTable, é necessário encontrar uma forma de explicitamente descartá-lo quando seu objeto pai é coletado. A classe ConditionalWeakTable não fará este trabalho automaticamente e confiar no finalizador é arriscado.