BT

Disseminando conhecimento e inovação em desenvolvimento de software corporativo.

Contribuir

Tópicos

Escolha a região

Início Notícias O futuro do C#: Variáveis Imutáveis

O futuro do C#: Variáveis Imutáveis

Em C#, a palavra reservada readonly só pode ser utilizada com construções de campos. A proposta 115, Readonly for Locals and Parameterers (somente leitura para variáveis locais e parâmetros) define extensões de uso da palavra reservada readonly para cobrir muitos outros cenários.

A proposta começa definindo a capacidade de criar variáveis locais readonly. O primeiro caso de uso observado é uma melhoria semântica para documentação. Ao modificar uma variável local para readonly, indica-se que esta variável não é e nunca deve ser alterada em nenhum outro lugar da função. Esta característica é especialmente útil para funções mais longas e complexas, que não podem ser examinadas em sua totalidade em uma única visão.

O segundo caso de uso é maior segurança ao trabalhar com closures e múltiplas threads. Ao executar Parallel.ForEach na closure, pode-se facilmente introduzir uma condição de corrida (race condition). Ao modificar as variáveis locais com readonly por padrão, qualquer variável local mutável vai ser apontada pelo compilador como uma condição a ser revisada.

Preocupações com a sintaxe

Variáveis locais readonly apresentam maior valor quando utilizadas por padrão. Mas, para que isso aconteça, a sintaxe não pode ser onerosa. Considere as linhas a seguir:

var gravity = 9.780327;
double gravity = 9.780327;
const double gravity = 9.780327;

Mesmo que a intenção seja de declarar a gravidade como uma constante, a maioria dos desenvolvedores prefere usar a primeira versão apresentada. Eles não fazem "a coisa certa" por que a alternativa adotada é mais simples de se digitar e, em um caso isolado, não apresenta tanta diferença.

Essa preferência de facilidade sobre corretude também ocorre nas operações de coerção de tipos. O código a seguir apresenta um erro muito comum mesmo para programadores experientes:

var button = sender as Button;
button.Enabled = false;

O código deveria ser "button = (Button)sender", mas novamente o código correto é ligeiramente mais trabalhoso de se digitar.

Para endereçar essas preocupações, a proposta sugere um atalho ao utilizar variáveis locais de tipos implícitos. Duas construções são consideradas:

val gravity = 9.780327;
let gravity = 9.780327;

Entre as duas opções, "let" está atualmente em preferência, pois ela já é utilizada em expressões LINQ e é simples de diferenciar visualmente de "var". Ela também é uma melhor opção para pessoas que não falam inglês e cuja língua nativa não distingue entre r e l.

Parâmetros de somente leitura

O próximo tema envolve a possibilidade de alterar parâmetros com a palavra reservada readonly. Usuários das ferramentas de análise de código do Visual Studio provavelmente considerarão isto redundante, uma vez que as ferramentas previnem a alteração de valor de um parâmetro normal. Ainda assim, existe um caso de uso que estas ferramentas não cobrem.

Ao trabalhar com código de alto desempenho é comum preferir o uso de estruturas (structs) ao invés de classes, mesmo quando estas estruturas são grandes. Para evitar os custos associados a cópias, estas estruturas são passadas para funções usando parâmetros por referência (ref parameters).

Do ponto de vista de documentação, não há nada na assinatura da função que deixe claro para o código que a invocou que o parâmetro não vá ser modificado. A possibilidade de marcar o parâmetro como "readonly ref" cobriria esta lacuna.

Nem todos os envolvidos estão contentes com a sintaxe, pois ela requer que o ponto de invocação seja decorado com a palavra reservada ref, o que pode ser de alguma ilusório. Ao invés disso, Porges sugere usar a palavra reservada "in":

void DoSomething(readonly ref LargeStruct value)
DoSomething(ref myLocal);
void DoSomething(in LargeStruct value)
DoSomething(myLocal);

Readonly e const

Enquanto a utilização de readonly previne a substituição direta de um valor, ela usualmente não previne a possibilidade de modificar um membro de um objeto. Desta forma, uma extensão a essa proposta é a capacidade de aumentar a proteção oferecida por readonly.

Existem linguagens como C++ que já suportam esse conceito. Apesar de funcionar corretamente, ele pode ser difícil de usar, pois os desenvolvedores frequentemente se confundem sobre qual é o contexto em que const está sendo aplicado: sobre a própria variável ou sobre o conteúdo da variável. Para evitar esta ambiguidade é importante considerar aspectos da sintaxe. Uma sugestão é usar readonly para a variável e const para seu conteúdo.

C++ também suporta o conceito de função const. Estas são funções que podem ser invocadas por meio de uma variável readonly/const pois isto garante que o estado do objeto não será alterado. .NET também suporta este conceito por meio do atributo Pure, mas ele não é atualmente considerado pelo compilador de C#.

Notas: readonly, structs e fields

Apesar de atualmente não fazer parte da proposta, a interação entre readonly, structs e fields deve ser levada em conta. Considere a linha a seguir:

 
private readonly Foo _foo = new Foo(1, 2, 3);

Se Foo for completamente imutável, então este campo readonly funciona conforme esperado. Mas, se existir qualquer setter em Foo, então cada vez que acessar este campo o compilador criará uma cópia para que não possa ser modificada acidentalmente. Ao contrário dos campos referenciados como readonly, estruturas readonly realmente são somente leitura. Mas, como existe um custo e inconsistência de desempenho de forma oculta, encontrar uma melhor semântica para expressar esse conceito seria benéfico.

Para mais informações, veja Mutating Readonly Structs de Eric Libbert.

Conteúdo educacional

BT