Quais são os benefícios de marcar um campo como `somente leitura 'em C #?

295

Quais são os benefícios de ter uma variável de membro declarada como somente leitura? É apenas uma proteção contra alguém que altera seu valor durante o ciclo de vida da classe ou o uso dessa palavra-chave resulta em melhorias de velocidade ou eficiência?

leora
fonte
8
Boa resposta externa: dotnetperls.com/readonly
OneWorld
3
Interessante. Esta é essencialmente a C # equivalente a esta pergunta Java stackoverflow.com/questions/137868/... discussão aqui é muito menos aquecido embora ... hmm ...
RAY
7
Pode-se notar que os readonlycampos dos tipos de estrutura impõem uma penalidade de desempenho em comparação com os campos mutáveis ​​que simplesmente não são mutados, pois a chamada de qualquer membro de um readonlycampo de tipo de valor fará com que o compilador faça uma cópia do campo e invoque o membro nisso.
Supercat 28/12 /
2
mais sobre a penalidade de desempenho: codeblog.jonskeet.uk/2014/07/16/…
CAD bloke

Respostas:

171

A readonlypalavra-chave é usada para declarar uma variável de membro uma constante, mas permite que o valor seja calculado em tempo de execução. Isso difere de uma constante declarada com o constmodificador, que deve ter seu valor definido no tempo de compilação. Usando readonlyvocê pode definir o valor do campo na declaração ou no construtor do objeto do qual o campo é membro.

Use-o também se não desejar recompilar DLLs externas que fazem referência à constante (pois ela é substituída no momento da compilação).

Bill the Lizard
fonte
193

Não acredito que haja ganhos de desempenho ao usar um campo somente leitura. É simplesmente uma verificação para garantir que, uma vez que o objeto esteja totalmente construído, esse campo não possa ser apontado para um novo valor.

No entanto, "somente leitura" é muito diferente de outros tipos de semântica somente leitura porque é imposta em tempo de execução pelo CLR. A palavra-chave readonly é compilada em .initonly, que é verificável pelo CLR.

A vantagem real dessa palavra-chave é gerar estruturas de dados imutáveis. Estruturas de dados imutáveis ​​por definição não podem ser alteradas depois de construídas. Isso facilita muito o raciocínio sobre o comportamento de uma estrutura em tempo de execução. Por exemplo, não há perigo de passar uma estrutura imutável para outra parte aleatória do código. Eles nunca podem mudar isso para que você possa programar de forma confiável com base nessa estrutura.

Aqui está uma boa entrada sobre um dos benefícios da imutabilidade: rosqueamento

JaredPar
fonte
6
Se você ler isso, stackoverflow.com/questions/9860595/... ler único membro pode ser modificado e se parece com um behavioir inconsistente por .net
Akash Kava
Você pode atualizar o link para a postagem no Threading acima? Está quebrado.
Akshay Khot
69

Não há benefícios aparentes no desempenho do uso readonly, pelo menos nenhum que eu já tenha mencionado em qualquer lugar. É apenas para fazer exatamente o que você sugere, para impedir a modificação depois que ela for inicializada.

Portanto, é benéfico, pois ajuda a escrever um código mais robusto e legível. O benefício real de coisas como essa ocorre quando você está trabalhando em equipe ou em manutenção. Declarar algo readonlysemelhante a colocar um contrato para o uso dessa variável no código. Pense nisso como adicionar documentação da mesma maneira que outras palavras-chave como internalou private, você está dizendo "essa variável não deve ser modificada após a inicialização" e, além disso, você a está aplicando .

Portanto, se você criar uma classe e marcar algumas variáveis ​​de membro readonlypor design, evite que você ou outro membro da equipe cometa um erro mais tarde ao expandir ou modificar sua classe. Na minha opinião, esse é um benefício que vale a pena ter (às custas da complexidade extra do idioma, como a doofledorfer menciona nos comentários).

Xiaofu
fonte
E também desimplifica o idioma. Não negando sua declaração de benefício, no entanto.
dkretz
3
Concordo, mas suponho que o benefício real ocorra quando mais de uma pessoa estiver trabalhando no código. É como ter uma pequena declaração de design dentro do código, um contrato para seu uso. Eu provavelmente deveria colocar isso na resposta, hehe.
Xiaofu
7
Esta resposta e discussão é realmente a melhor resposta na minha opinião +1
Jeff Martin
@Xiaofu: Você me fez constante da idéia de somente leitura hahaha bela explicação que ninguém neste mundo poderia explicar a mente silliest um entender
Learner
Ou seja, você mantém a intenção no seu código de que esse valor não deve ser alterado a qualquer momento.
Andez
51

Para colocar em termos muito práticos:

Se você usar uma const na DLL A e na DLL B fizer referência a essa const, o valor dessa const será compilado na DLL B. Se você reimplementar a DLL A com um novo valor para essa const, a DLL B continuará usando o valor original.

Se você usar um readonly nas referências da DLL A e da DLL B que somente leitura, essa leitura sempre será pesquisada em tempo de execução. Isso significa que, se você reimplementar a DLL A com um novo valor somente para isso, a DLL B usará esse novo valor.

Daniel Auger
fonte
4
Este é um bom exemplo prático para entender a diferença. Obrigado.
Shyju
Por outro lado, constpode ter ganho de desempenho acima readonly. Aqui está uma explicação um pouco mais profunda com o código: dotnetperls.com/readonly
Dio Phung
2
Acho que o maior termo prático está faltando nessa resposta: a capacidade de armazenar valores calculados em tempo de execução em readonlycampos. Você não pode armazenar um new object();em um conste isso faz sentido porque você não pode criar coisas sem valor, como referências em outros assemblies durante o tempo de compilação sem alterar a identidade.
binki
14

Há um caso potencial em que o compilador pode fazer uma otimização de desempenho com base na presença da palavra-chave somente leitura.

Isso se aplica apenas se o campo somente leitura também estiver marcado como estático . Nesse caso, o compilador JIT pode assumir que esse campo estático nunca será alterado. O compilador JIT pode levar isso em consideração ao compilar os métodos da classe.

Exemplo típico: sua classe pode ter um campo IsDebugLoggingEnabled estático, somente leitura , que é inicializado no construtor (por exemplo, com base em um arquivo de configuração). Depois que os métodos reais são compilados JIT, o compilador pode omitir partes inteiras do código quando o log de depuração não está ativado.

Eu não verifiquei se essa otimização está realmente implementada na versão atual do compilador JIT, então isso é apenas especulação.

Kristof Verbiest
fonte
existe uma fonte para isso?
Sedat Kapanoglu
4
O atual compilador JIT, de fato, implementa isso e tem desde o CLR 3.5. github.com/dotnet/coreclr/issues/1079
mirhagk
Nenhuma otimização pode ser feita em campos somente leitura pelo simples motivo de que os campos somente leitura não são somente leitura, mas são de gravação. Eles são apenas uma sugestão do compilador de que a maioria dos compiladores respeita e os valores dos campos somente leitura podem ser facilmente substituídos por reflexão (embora não em código parcialmente confiável).
Christoph
9

Lembre-se de que o readonly se aplica apenas ao valor em si; portanto, se você estiver usando um tipo de referência, o readonly apenas protegerá a referência de alterações. O estado da instância não é protegido por somente leitura.

Brian Rasmussen
fonte
4

Não esqueça que existe uma solução alternativa para obter os readonlycampos definidos fora de qualquer construtor usando outparâmetros.

Um pouco confuso, mas:

private readonly int _someNumber;
private readonly string _someText;

public MyClass(int someNumber) : this(data, null)
{ }

public MyClass(int someNumber, string someText)
{
    Initialise(out _someNumber, someNumber, out _someText, someText);
}

private void Initialise(out int _someNumber, int someNumber, out string _someText, string someText)
{
    //some logic
}

Discussões adicionais aqui: http://www.adamjamesnaylor.com/2013/01/23/Setting-Readonly-Fields-From-Chained-Constructors.aspx

Adam Naylor
fonte
4
Os campos ainda estão atribuídos no construtor .. não há como "contornar" isso. Não importa se os valores são a partir de expressões individuais, a partir de um tipo complexo decomposto, ou atribuído através de chamada por semântica de referência de out..
user2864740
Isso nem tenta responder à pergunta.
Sheridan
2

Surpreendentemente, somente leitura pode resultar em código mais lento, como Jon Skeet descobriu ao testar sua biblioteca Noda Time. Nesse caso, um teste executado em 20 segundos levou apenas 4 segundos após a remoção somente leitura.

https://codeblog.jonskeet.uk/2014/07/16/micro-optimization-the-surprising-inefficiency-of-readonly-fields/

Neil
fonte
3
Observe que se o campo for do tipo readonly structC # 7.2, o benefício de tornar o campo não-somente leitura desaparece.
21818 Jon Skeet
1

Se você possui um valor pré-definido ou pré-calculado que precisa permanecer o mesmo durante todo o programa, deve-se usar constante, mas se tiver um valor que precise ser fornecido no tempo de execução, mas, uma vez atribuído, deverá permanecer o mesmo durante todo o programa. somente leitura. por exemplo, se você precisar atribuir a hora de início do programa ou armazenar um valor fornecido pelo usuário na inicialização do objeto e precisar restringi-lo de outras alterações, use somente leitura.

waqar ahmed
fonte
1

Adicionando um aspecto básico para responder a esta pergunta:

As propriedades podem ser expressas como somente leitura, deixando de fora o setoperador. Portanto, na maioria dos casos, você não precisará adicionar a readonlypalavra-chave às propriedades:

public int Foo { get; }  // a readonly property

Em contraste com isso: os campos precisam da readonlypalavra-chave para obter um efeito semelhante:

public readonly int Foo; // a readonly field

Portanto, um benefício de marcar um campo como readonlypode ser obter um nível de proteção contra gravação semelhante a uma propriedade sem setoperador - sem ter que alterar o campo para uma propriedade, se por qualquer motivo, isso for desejado.

code14214
fonte
Existe uma diferença de comportamento entre os 2?
petrosmm
0

Cuidado com matrizes particulares somente de leitura. Se eles forem expostos a um cliente como um objeto (você pode fazer isso para interoperabilidade COM como eu fiz), o cliente poderá manipular os valores da matriz. Use o método Clone () ao retornar uma matriz como um objeto.

Michael
fonte
20
Não; expor um em ReadOnlyCollection<T>vez de uma matriz.
SLaks
Isso deve ser um comentário, não uma resposta, uma vez que não fornece resposta para a pergunta ... #
Peter Peter
Curiosamente, me disseram para colocar esse tipo de coisa como uma resposta, em vez de um comentário quando o fiz em outro post na semana passada.
Kyle Baran
A partir de 2013, você pode usar ImmutableArray<T>, o que evita o boxe para uma interface ( IReadOnlyList<T>) ou quebra de classe ( ReadOnlyCollection). Ele tem desempenho comparável ao matrizes nativas: blogs.msdn.microsoft.com/dotnet/2013/06/24/...
Charles Taylor
0

Pode haver um benefício de desempenho no WPF, pois elimina a necessidade de DependencyProperties caras. Isso pode ser especialmente útil com coleções

Shane
fonte
0

Outra parte interessante do uso da marcação somente leitura pode proteger o campo da inicialização no singleton.

por exemplo, no código de csharpindepth :

public sealed class Singleton
{
    private static readonly Lazy<Singleton> lazy =
        new Lazy<Singleton>(() => new Singleton());

    public static Singleton Instance { get { return lazy.Value; } }

    private Singleton()
    {
    }
}

O readonly desempenha um pequeno papel de proteger o campo Singleton de ser inicializado duas vezes. Outro detalhe é que, para o cenário mencionado, você não pode usar const, porque const força a criação durante o tempo de compilação, mas o singleton faz a criação em tempo de execução.

Yuriy Zaletskyy
fonte
0

readonlypode ser inicializado na declaração ou obter seu valor apenas do construtor. Ao contrário const, deve ser inicializado e declarado ao mesmo tempo. readonly tem tudo const , além da inicialização do construtor

código https://repl.it/HvRU/1

using System;

class MainClass {
    public static void Main (string[] args) {

        Console.WriteLine(new Test().c);
        Console.WriteLine(new Test("Constructor").c);
        Console.WriteLine(new Test().ChangeC()); //Error A readonly field 
        // `MainClass.Test.c' cannot be assigned to (except in a constructor or a 
        // variable initializer)
    }


    public class Test {
        public readonly string c = "Hello World";
        public Test() {

        }

        public Test(string val) {
          c = val;
        }

        public string ChangeC() {
            c = "Method";
            return c ;
        }
    }
}
Mina Gabriel
fonte