¿Cuáles son los beneficios de marcar un campo como `readonly` en C #?

295

¿Cuáles son los beneficios de tener una variable miembro declarada como de solo lectura? ¿Es solo protección contra alguien que cambia su valor durante el ciclo de vida de la clase o el uso de esta palabra clave resulta en mejoras de velocidad o eficiencia?

leora
fuente
8
Buena respuesta externa: dotnetperls.com/readonly
OneWorld
3
Interesante. Esto es esencialmente el equivalente en C # de esta pregunta de Java stackoverflow.com/questions/137868/… La discusión aquí es mucho menos acalorada aunque ... hmm ...
RAY
77
Vale la pena señalar que los readonlycampos de tipos de estructura imponen una penalización de rendimiento en comparación con los campos mutables que simplemente no están mutados, ya que la invocación de cualquier miembro de un readonlycampo de tipo de valor hará que el compilador haga una copia del campo e invoque el miembro de eso.
supercat
2
más sobre la penalización de rendimiento: codeblog.jonskeet.uk/2014/07/16/…
CAD

Respuestas:

171

La readonlypalabra clave se utiliza para declarar una variable miembro como constante, pero permite que el valor se calcule en tiempo de ejecución. Esto difiere de una constante declarada con el constmodificador, que debe tener su valor establecido en tiempo de compilación. Utilizando readonlypuede establecer el valor del campo en la declaración o en el constructor del objeto del que es miembro el campo.

Úselo también si no desea tener que volver a compilar archivos DLL externos que hacen referencia a la constante (ya que se reemplaza en tiempo de compilación).

Bill el lagarto
fuente
193

No creo que haya ganancias de rendimiento al usar un campo de solo lectura. Es simplemente una comprobación para garantizar que una vez que el objeto esté completamente construido, ese campo no pueda apuntar a un nuevo valor.

Sin embargo, "solo lectura" es muy diferente de otros tipos de semántica de solo lectura porque el CLR lo aplica en tiempo de ejecución. La palabra clave readonly se compila en .initonly, que es verificable por el CLR.

La verdadera ventaja de esta palabra clave es generar estructuras de datos inmutables. Las estructuras de datos inmutables por definición no se pueden cambiar una vez construidas. Esto hace que sea muy fácil razonar sobre el comportamiento de una estructura en tiempo de ejecución. Por ejemplo, no hay peligro de pasar una estructura inmutable a otra porción aleatoria de código. Nunca pueden cambiarlo, por lo que puede programar de manera confiable contra esa estructura.

Aquí hay una buena entrada sobre uno de los beneficios de la inmutabilidad: Subprocesamiento

JaredPar
fuente
66
Si lee esto, stackoverflow.com/questions/9860595/… miembro de solo lectura puede ser modificado y parece un comportamiento inconsistente por .net
Akash Kava
¿Puedes actualizar el enlace a la publicación en Threading anterior? Esta roto.
Akshay Khot
69

No hay beneficios aparentes de rendimiento al usar readonly, al menos ninguno que haya visto mencionado en ninguna parte. Es solo para hacer exactamente lo que sugiere, para evitar la modificación una vez que se ha inicializado.

Por lo tanto, es beneficioso porque te ayuda a escribir código más robusto y más legible. El beneficio real de cosas como esta viene cuando trabajas en equipo o por mantenimiento. Declarar algo como readonlyes similar a poner un contrato para el uso de esa variable en el código. Piense en ello como agregar documentación de la misma manera que otras palabras clave como internalo private, está diciendo "esta variable no debe modificarse después de la inicialización", y además la está aplicando .

Entonces, si crea una clase y marca algunas variables de miembros readonlypor diseño, entonces evita que usted u otro miembro del equipo cometa un error más adelante cuando están expandiendo o modificando su clase. En mi opinión, es un beneficio que vale la pena tener (a costa de la complejidad adicional del lenguaje, como menciona doofledorfer en los comentarios).

Xiaofu
fuente
Y otoh simplifica el lenguaje. Sin embargo, no niega su declaración de beneficios.
dkretz
3
Estoy de acuerdo, pero supongo que el beneficio real llega cuando más de una persona está trabajando en el código. Es como tener una pequeña declaración de diseño dentro del código, un contrato para su uso. Probablemente debería poner eso en la respuesta, jeje.
Xiaofu
77
Esta respuesta y discusión es en realidad la mejor respuesta en mi opinión +1
Jeff Martin
@Xiaofu: Me hiciste constante de la idea de solo lectura jajaja hermosa explicación de que nadie en este mundo podría explicar la mente más tonta y entender
Estudiante el
Es decir, está intentando mantener en su código que este valor no debería cambiar en ningún momento.
Andez
51

Para ponerlo en términos muy prácticos:

Si usa una constante en dll A y dll B hace referencia a esa constante, el valor de esa constante se compilará en dll B. Si vuelve a implementar dll A con un nuevo valor para esa constante, dll B seguirá utilizando el valor original.

Si utiliza un solo lectura en las referencias dll A y dll B que solo leen, esa lectura siempre se buscará en tiempo de ejecución. Esto significa que si vuelve a implementar dll A con un nuevo valor para esa lectura solamente, dll B usará ese nuevo valor.

Daniel Auger
fuente
44
Este es un buen ejemplo práctico para entender la diferencia. Gracias.
Shyju
Por otro lado, constpuede tener una mejora en el rendimiento readonly. Aquí hay una explicación un poco más profunda con el código: dotnetperls.com/readonly
Dio Phung
2
Creo que el término práctico más grande falta en esta respuesta: la capacidad de almacenar valores calculados en tiempo de ejecución en readonlycampos. No puede almacenar un new object();en ay consteso tiene sentido porque no puede hornear cosas sin valor como referencias en otros ensamblajes durante el tiempo de compilación sin cambiar la identidad.
binki
14

Existe un caso potencial en el que el compilador puede realizar una optimización del rendimiento en función de la presencia de la palabra clave de solo lectura.

Esto solo se aplica si el campo de solo lectura también está marcado como estático . En ese caso, el compilador JIT puede asumir que este campo estático nunca cambiará. El compilador JIT puede tener esto en cuenta al compilar los métodos de la clase.

Ejemplo típico: su clase podría tener un campo estático de solo lectura IsDebugLoggingEnabled que se inicializa en el constructor (por ejemplo, basado en un archivo de configuración). Una vez que se compilan los métodos reales JIT, el compilador puede omitir partes enteras del código cuando el registro de depuración no está habilitado.

No he comprobado si esta optimización se implementa realmente en la versión actual del compilador JIT, por lo que esto es solo especulación.

Kristof Verbiest
fuente
¿Hay alguna fuente para esto?
Sedat Kapanoglu
44
De hecho, el compilador JIT actual implementa esto, y lo ha hecho desde CLR 3.5. github.com/dotnet/coreclr/issues/1079
mirhagk
No se pueden realizar optimizaciones en los campos de solo lectura por la sencilla razón de que los campos de solo lectura no son de solo lectura sino de lectura y escritura. Son solo una pista del compilador que la mayoría de los compiladores respetan y los valores de los campos de solo lectura pueden sobrescribirse fácilmente mediante la reflexión (aunque no en un código de confianza parcial).
Christoph
9

Tenga en cuenta que solo lectura solo se aplica al valor en sí mismo, por lo que si está utilizando un tipo de referencia solo lectura solo protege la referencia de ser cambiada. El estado de la instancia no está protegido por readonly.

Brian Rasmussen
fuente
4

No olvide que hay una solución alternativa para readonlyconfigurar los campos fuera de cualquier constructor que use outparámetros.

Un poco desordenado pero:

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
}

Más discusión aquí: http://www.adamjamesnaylor.com/2013/01/23/Setting-Readonly-Fields-From-Chained-Constructors.aspx

Adam Naylor
fuente
44
Los campos todavía están asignados en el constructor ... no hay "moverse" eso. No importa si los valores provienen de expresiones individuales, de un tipo complejo descompuesto, o asignados por llamada mediante la semántica de referencia de out..
user2864740
Esto ni siquiera intenta responder la pregunta.
Sheridan
2

Sorprendentemente, solo lectura puede resultar en un código más lento, como descubrió Jon Skeet al probar su biblioteca Noda Time. En este caso, una prueba que se ejecutó en 20 segundos tomó solo 4 segundos después de eliminar solo lectura.

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

Neil
fuente
3
Tenga en cuenta que si el campo es de un tipo que está readonly structen C # 7.2, el beneficio de hacer que el campo no sea de solo lectura desaparece.
Jon Skeet
1

Si tiene un valor predefinido o precalculado que debe permanecer igual durante todo el programa, entonces debe usar constante, pero si tiene un valor que debe proporcionarse en el tiempo de ejecución pero una vez asignado debe permanecer igual en todo el programa, debe usar solo lectura. por ejemplo, si tiene que asignar la hora de inicio del programa o si tiene que almacenar un valor proporcionado por el usuario en la inicialización del objeto y tiene que restringirlo de más cambios, debe usar solo lectura.

waqar ahmed
fuente
1

Agregar un aspecto básico para responder esta pregunta:

Las propiedades se pueden expresar como de solo lectura al omitir el setoperador. Por lo tanto, en la mayoría de los casos no necesitará agregar la readonlypalabra clave a las propiedades:

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

En contraste con eso: los campos necesitan la readonlypalabra clave para lograr un efecto similar:

public readonly int Foo; // a readonly field

Por lo tanto, una ventaja de marcar un campo como readonlypuede ser lograr un nivel de protección contra escritura similar a una propiedad sin setoperador, sin tener que cambiar el campo a una propiedad, si por alguna razón, se desea.

code14214
fuente
¿Hay alguna diferencia de comportamiento entre los 2?
petrosmm
0

Tenga cuidado con las matrices privadas de solo lectura. Si estos están expuestos a un cliente como un objeto (puede hacer esto para la interoperabilidad COM como lo hice), el cliente puede manipular los valores de la matriz. Use el método Clone () cuando devuelva una matriz como un objeto.

Miguel
fuente
20
No; exponer un en ReadOnlyCollection<T>lugar de una matriz.
SLaks
Esto debería ser un comentario, no una respuesta, ya que no proporciona respuesta a la pregunta ...
Peter
Curiosamente, me dijeron que pusiera este tipo de cosas como una respuesta, en lugar de un comentario cuando lo hice en otra publicación la semana pasada.
Kyle Baran
A partir de 2013, puede usar ImmutableArray<T>, lo que evita el boxeo a una interfaz ( IReadOnlyList<T>) o el ajuste en una clase ( ReadOnlyCollection). Tiene un rendimiento comparable a las matrices nativas: blogs.msdn.microsoft.com/dotnet/2013/06/24/…
Charles Taylor
0

Puede haber un beneficio de rendimiento en WPF, ya que elimina la necesidad de costosas propiedades de dependencia. Esto puede ser especialmente útil con colecciones

Shane
fuente
0

Otra parte interesante del uso de marcado de solo lectura puede ser proteger el campo de la inicialización en singleton.

por ejemplo en el 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()
    {
    }
}

readonly juega un pequeño papel en la protección del campo Singleton para que no se inicialice dos veces. Otro detalle es que para el escenario mencionado no puede usar const porque const fuerza la creación durante el tiempo de compilación, pero singleton hace la creación en tiempo de ejecución.

Yuriy Zaletskyy
fuente
0

readonlypuede inicializarse en la declaración u obtener su valor solo del constructor. A diferencia de constesto, debe inicializarse y declararse al mismo tiempo. readonly tiene todo const , más inicialización del constructor

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
fuente