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

Favoritos

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.

Avalie esse artigo

Relevância
Estilo/Redação

Conteúdo educacional

BT