Por que o C # não permite variáveis ​​locais somente leitura?

115

Ter um debate amigável com um colega de trabalho sobre isso. Temos algumas ideias sobre isso, mas quer saber o que a multidão do SO pensa sobre isso?

Brian Genisio
fonte
4
@ColonelPanic C e C ++ têm variáveis ​​locais const, que você pode inicializar com um valor calculado em tempo de execução.
Crashworks de
1
JavaScript 2015 (ES6) tem tipo const. Por exemplo, {const myList = [1,2,3]; } É uma prática de programação muito boa usar essa construção. Mais informações: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
andrew.fox
1
Para os interessados, há uma sugestão do UserVoice para esse recurso . No momento, ele está com 87 votos, então se você quiser ver as variáveis ​​locais somente leitura, vá em frente!
Ian Kemp,
1
Não é apenas um problema de idioma. É um problema da maioria na comunidade C #, incluindo os gurus C # mais bem avaliados, que eles não se importem com a correção const e qualquer coisa relacionada a ela. Resistir é inútil.
Patrick Fromberg
1
Atualização de 2017 : vote na solicitação de recurso em discussão no repositório C # Language Design! github.com/dotnet/csharplang/issues/188
Coronel Panic

Respostas:

15

Um dos motivos é que não há suporte CLR para um local somente leitura. Readonly é traduzido no opcode initonly CLR / CLI. Este sinalizador só pode ser aplicado a campos e não tem significado para um local. Na verdade, aplicá-lo a um local provavelmente produzirá um código não verificável.

Isso não significa que C # não possa fazer isso. Mas daria dois significados diferentes para a mesma construção de linguagem. A versão para locais não teria mapeamento equivalente de CLR.

JaredPar
fonte
57
Na verdade, não tem nada a ver com o suporte CLI para o recurso, porque as variáveis ​​locais não são de forma alguma expostas a outros assemblies. A readonlypalavra-chave para os campos precisa ser suportada pela CLI porque seu efeito é visível para outros assemblies. Significa apenas que a variável tem apenas uma atribuição no método em tempo de compilação.
Sam Harwell
16
Acho que você acabou de mudar a questão de por que o CLR não apóia isso, em vez de fornecer a razão por trás disso. Ele permite locais const, portanto, seria razoável esperar locais somente leitura também.
Chad Schouggins de
9
Um exemplo disso são as variáveis ​​definidas em uma instrução using. Eles são locais ... e somente leitura (tente atribuí-los, C # adicionará um erro).
Softlion
7
-1 Em C ++ não há suporte para código de máquina const(que em C ++ é mais parecido com C # do readonlyque com C # const, embora possa desempenhar ambas as funções). Ainda assim, C ++ oferece suporte constpara variável automática local. Conseqüentemente, a falta de suporte CLR para um C # readonlypara variável local é irrelevante.
Saúde e hth. - Alf de
5
1. Isso pode ser facilmente um recurso do compilador, como em C ++. O suporte CLR é completamente irrelevante. A montagem da máquina também não oferece suporte, e daí? 2. (iria) provavelmente produzir código não verificável - Não vejo como, mas talvez esteja enganado. 3. daria dois significados diferentes para a mesma construção de linguagem - duvido que alguém veria isso como um problema, já que usinge outestão fazendo exatamente isso e o mundo não entrou em colapso.
Lou
66

Acho que é um julgamento pobre por parte dos arquitetos C #. o modificador somente leitura em variáveis ​​locais ajuda a manter a correção do programa (assim como afirma) e pode ajudar potencialmente o compilador a otimizar o código (pelo menos no caso de outras linguagens). O fato de não ser permitido no C # agora é outro argumento de que alguns dos "recursos" do C # são meramente uma imposição do estilo de codificação pessoal de seus criadores.

andriej
fonte
11
Concordo com a parte "salvar o programador de si mesmo", mas quanto a ajudar o compilador a otimizar o código, defendo que o compilador pode descobrir muito bem se uma variável muda ou não ao longo do método e otimiza de acordo de qualquer jeito. Colocar um sinalizador 'somente leitura' antes de algo que o otimizador reconhece de qualquer forma para esse propósito não beneficia realmente, mas potencialmente engana.
Cornelius
1
@Cornelius Concordo que há opiniões de que, em alguns casos, o compilador usa o diagrama de fluxo de dados para descobrir a oportunidade de otimização, independentemente de quaisquer palavras-chave / modificadores. Mas evitar que o programador escreva códigos incorretos e desnecessariamente não otimizados pode abrir essa oportunidade de otimização para o compilador.
shuva de
Os compiladores modernos não executam a Atribuição única estática de qualquer maneira? Nesse caso, é discutível no que diz respeito às otimizações (mas se um compilador suporta SSA, então isso significa que também é trivial implementar variáveis ​​locais de atribuição única).
Dia
33

Respondendo à resposta de Jared, provavelmente teria que ser apenas um recurso de tempo de compilação - o compilador iria proibir você de escrever na variável após a declaração inicial (que teria que incluir uma atribuição).

Posso ver valor nisso? Potencialmente - mas não muito, para ser honesto. Se você não puder dizer facilmente se uma variável será ou não atribuída em outro lugar no método, então seu método é muito longo.

Pelo que vale a pena, Java tem esse recurso (usando o finalmodificador) e muito raramente o vi usado, exceto nos casos em que tem que ser usado para permitir que a variável seja capturada por uma classe interna anônima - e onde ela está usado, dá-me a impressão de desordem em vez de informação útil.

Jon Skeet
fonte
75
Há uma diferença entre ver se uma variável é ou não modificada em seu método pela vista e pelo compilador . Não vejo objeção em escrever um método, declarar minha intenção de não modificar uma variável e ter o compilador me notificando quando eu o fizer acidentalmente (talvez com um erro de digitação um mês depois)!
A. Rex
50
Por outro lado, no F # todas as variáveis ​​são somente leitura por padrão e você deve usar a palavra-chave 'mutable' se quiser alterá-las. Como F # é uma linguagem .NET, imagino que ele faça a verificação em tempo de compilação que você descreve.
Joel Mueller
2
@ A.Rex: A questão é realmente se o benefício de fazer com que o compilador faça essa verificação compensa o "buquê" extra ao ler o código e não se importar realmente com ele.
Jon Skeet
3
FWIW, Scala distingue local readonly/ finalvalores de variáveis ​​com suas palavras val- varchave e . No código Scala, os locais valsão usados ​​com muita frequência (e são, de fato, preferidos aos locais var). Suspeito que os principais motivos pelos quais o finalmodificador não é usado com mais frequência em Java são a) desordem eb) preguiça.
Aaron Novstrup de
4
Para variáveis ​​locais que não são usadas em fechamentos, readonlynão seria muito importante. Por outro lado, para variáveis ​​locais que são usadas em encerramentos, readonlyem muitos casos deixariam o compilador gerar um código mais eficiente. Atualmente, quando a execução entra em um bloco que contém um fechamento, o compilador deve criar um novo objeto heap para as variáveis ​​fechadas, mesmo se nenhum código que usaria o fechamento seja executado . Se uma variável fosse somente leitura, o código fora do encerramento poderia usar uma variável normal; apenas quando um delegado é criado para o encerramento ...
supercat
30

Uma proposta somente leitura de locais e parâmetros para foi brevemente discutida pela equipe de design do C # 7. De C # Design Meeting Notes para 21 de janeiro de 2015 :

Parâmetros e locais podem ser capturados por lambdas e, portanto, acessados ​​simultaneamente, mas não há como protegê-los de problemas de estado mútuo compartilhado: eles não podem ser somente leitura.

Em geral, a maioria dos parâmetros e muitos locais nunca devem ser atribuídos depois de obterem seu valor inicial. Permitir somente leitura neles expressaria essa intenção claramente.

Um problema é que esse recurso pode ser um "incômodo atraente". Considerando que a "coisa certa" a fazer quase sempre seria tornar os parâmetros e locais somente leitura, isso desordenaria o código significativamente.

Uma ideia para aliviar parcialmente isso é permitir que a combinação somente leitura var em uma variável local seja contraída para val ou algo curto assim. De maneira mais geral, poderíamos tentar simplesmente pensar em uma palavra-chave mais curta do que a somente leitura estabelecida para expressar o somente leitura.

A discussão continua no repositório C # Language Design. Vote para mostrar seu apoio. https://github.com/dotnet/csharplang/issues/188

Coronel Panic
fonte
também pode tornar somente leitura o padrão (por meio de alguma opção ou palavra-chave ou qualquer outra). a maioria das variáveis ​​e parâmetros devem ser somente leitura, com apenas alguns graváveis. e minimizar o número de graváveis ​​geralmente é uma coisa boa.
Dave Cousineau
12

É um descuido para o designer da linguagem c #. F # tem a palavra-chave val e é baseada no CLR. Não há razão para que C # não tenha o mesmo recurso de linguagem.

Derek Liang
fonte
7

Eu era aquele colega de trabalho e não era amigável! (só brincando)

Eu não eliminaria o recurso porque é melhor escrever métodos curtos. É um pouco como dizer que você não deve usar threads porque são difíceis. Dê-me a faca e deixe-me ser responsável por não me cortar.

Pessoalmente, eu queria outra palavra-chave do tipo "var" como "inv" (invariente) ou "rvar" para evitar confusão. Tenho estudado F # recentemente e acho a coisa imutável atraente.

Nunca soube que Java tinha isso.

Mike
fonte
5

Gostaria de variáveis locais somente leitura da mesma maneira que gosto de variáveis locais const . Mas tem menos prioridade do que outros tópicos.
Talvez sua prioridade seja a mesma razão para que os designers de C # (ainda!) Não implementem esse recurso. Mas deve ser fácil (e compatível com versões anteriores) suportar variáveis ​​locais somente leitura em versões futuras.

brgerner
fonte
2

Somente leitura significa que o único lugar em que a variável de instância pode ser definida é no construtor. Ao declarar uma variável localmente, ela não tem uma instância (está apenas no escopo) e não pode ser tocada pelo construtor.

Jason Punyon
fonte
6
Esse é o significado atual de 'somente leitura' em C #, mas essa não é a questão. 'read only' tem um significado em inglês que parece ter uma aplicação intuitiva para uma variável local: você não pode escrever nela (depois de inicializada). Isso se parece muito com o significado quando aplicado a variáveis ​​de instância, então por que (como na justificativa, eu acho) não podemos aplicá-lo a variáveis ​​locais?
Spike0xff
0

Eu sei, isso não responde o porquê da sua pergunta. De qualquer forma, aqueles que estão lendo esta pergunta podem apreciar o código abaixo.

Se você está realmente preocupado em dar um tiro no próprio pé ao substituir uma variável local que deve ser definida apenas uma vez, e não quer torná-la uma variável mais acessível globalmente, você poderia fazer algo assim.

    public class ReadOnly<T>
    {
        public T Value { get; private set; }

        public ReadOnly(T pValue)
        {
            Value = pValue;
        }

        public static bool operator ==(ReadOnly<T> pReadOnlyT, T pT)
        {
            if (object.ReferenceEquals(pReadOnlyT, null))
            {
                return object.ReferenceEquals(pT, null);
            }
            return (pReadOnlyT.Value.Equals(pT));
        }

        public static bool operator !=(ReadOnly<T> pReadOnlyT, T pT)
        {
            return !(pReadOnlyT == pT);
        }
    }

Exemplo de uso:

        var rInt = new ReadOnly<int>(5);
        if (rInt == 5)
        {
            //Int is 5 indeed
        }
        var copyValueOfInt = rInt.Value;
        //rInt.Value = 6; //Doesn't compile, setter is private

Talvez não menos código, rvar rInt = 5mas funciona.

Mike de Klerk
fonte
Isso não ajuda aqui. Este problema com a variável 'var' é: {var cinco = 5 cinco = 6; Assert.That (five == 5)}
Murray
0

Você pode declarar variáveis ​​locais somente leitura em C #, se estiver usando o compilador interativo C # csi:

>"C:\Program Files (x86)\MSBuild\14.0\Bin\csi.exe"
Microsoft (R) Visual C# Interactive Compiler version 1.3.1.60616
Copyright (C) Microsoft Corporation. All rights reserved.

Type "#help" for more information.
> readonly var message = "hello";
> message = "goodbye";
(1,1): error CS0191: A readonly field cannot be assigned to (except in a constructor or a variable initializer)

Você também pode declarar variáveis ​​locais somente leitura no .csxformato de script.

Coronel Panic
fonte
5
Conforme a mensagem de erro, messagenão é uma variável aqui, ela é compilada em um campo. Isso não é picuinhas porque a distinção existe claramente no C # interativo também: int x; Console.WriteLine(x)é C # interativo legal (porque xé um campo e inicializado implicitamente), mas void foo() { int x; Console.WriteLine(x); }não é (porque xé uma variável e usada antes de ser atribuída). Além disso, Expression<Func<int>> y = x; ((MemberExpression) y.Body).Member.MemberTyperevelará que xé realmente um campo e não uma variável local.
Jeroen Mostert
0

c # já tem um var somente leitura, embora em uma sintaxe um pouco diferente:

Considere as seguintes linhas:

var mutable = myImmutableCalculationMethod();
readonly var immutable = mutable; // not allowed in C# 8 and prior versions
return immutable;

Compare com:

var mutable = myImmutableCalculationMethod();
string immutable() => mutable; // allowed in C# 7
return immutable();

É certo que a primeira solução poderia ser menos código para escrever. Mas o segundo fragmento tornará o somente leitura explícito, ao fazer referência à variável.

Wolfgang Grinfeld
fonte
Isso não é "somente leitura". Essa é uma função local que retorna uma variável capturada ... então, muita sobrecarga desnecessária e sintaxe feia que nem mesmo torna a variável subjacente somente leitura, do ponto de vista de acesso ao código. Além disso, "mutável" geralmente se aplica a objetos, e não variáveis locais .. considerar: readonly var im = new List<string>(); im.Add("read-only variable, mutable object!");.
user2864740
-2

usar const palavra-chave para tornar a variável somente leitura.

referência: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/const

public class SealedTest
{
    static void Main()
    {
        const int c = 707;
        Console.WriteLine("My local constant = {0}", c);
    }
}
Derek Liang
fonte
1
Estamos interessados ​​no estilo JavaScript, constonde a variável só pode ser atribuída durante sua inicialização - não no estilo csharp, constonde apenas expressões de tempo de compilação podem ser usadas. Por exemplo, você não pode fazer, const object c = new object();mas um readonlylocal permitiria que você fizesse isso.
binki
-5

Acho que é porque uma função que tem uma variável somente leitura pode nunca ser chamada, e provavelmente há algo sobre ela saindo do escopo, e quando você precisaria?

Scottm
fonte