variable usar una solo propiedades propiedad property predefinidas metodos llamar lectura español ejemplos cuando clases clase c# struct value-type readonly-attribute

c# - usar - variable de solo lectura



¿El modificador ''solo lectura'' crea una copia oculta de un campo? (1)

La única diferencia entre las implementaciones de MutableSlab y ImmutableSlab es el modificador de readonly aplicado en el campo del handle :

using System; using System.Runtime.InteropServices; public class Program { class MutableSlab : IDisposable { private GCHandle handle; public MutableSlab() { this.handle = GCHandle.Alloc(new byte[256], GCHandleType.Pinned); } public bool IsAllocated => this.handle.IsAllocated; public void Dispose() { this.handle.Free(); } } class ImmutableSlab : IDisposable { private readonly GCHandle handle; public ImmutableSlab() { this.handle = GCHandle.Alloc(new byte[256], GCHandleType.Pinned); } public bool IsAllocated => this.handle.IsAllocated; public void Dispose() { this.handle.Free(); } } public static void Main() { var mutableSlab = new MutableSlab(); var immutableSlab = new ImmutableSlab(); mutableSlab.Dispose(); immutableSlab.Dispose(); Console.WriteLine($"{nameof(mutableSlab)}.handle.IsAllocated = {mutableSlab.IsAllocated}"); Console.WriteLine($"{nameof(immutableSlab)}.handle.IsAllocated = {immutableSlab.IsAllocated}"); } }

Pero producen resultados diferentes:

mutableSlab.handle.IsAllocated = False immutableSlab.handle.IsAllocated = True

GCHandle es una estructura mutable y cuando la copia, se comporta exactamente como en un escenario con immutableSlab .

¿El modificador de readonly crea una copia oculta de un campo? ¿Significa que no es solo una verificación en tiempo de compilación? No pude encontrar nada sobre este comportamiento here . ¿Está documentado este comportamiento?


¿El modificador de readonly crea una copia oculta de un campo?

Al llamar a un método o propiedad en un campo de solo lectura de un tipo de estructura normal (fuera del constructor o constructor estático) primero se copia el campo, sí. Esto se debe a que el compilador no sabe si el acceso a la propiedad o al método modificaría el valor al que lo llamas.

De la especificación C # 5 de ECMA :

Sección 12.7.5.1 (Acceso de miembros, general)

Esto clasifica los accesos de los miembros, incluyendo:

  • Si identifico un campo estático:
    • Si el campo es de solo lectura y la referencia se produce fuera del constructor estático de la clase o estructura en la que se declara el campo, el resultado es un valor, es decir, el valor del campo estático I en E.
    • De lo contrario, el resultado es una variable, es decir, el campo estático I en E.

Y:

  • Si T es un tipo de estructura e I identifica un campo de instancia de ese tipo de estructura:
    • Si E es un valor, o si el campo es de solo lectura y la referencia se produce fuera de un constructor de instancia de la estructura en la que se declara el campo, entonces el resultado es un valor, es decir, el valor del campo I en la instancia de estructura proporcionada por MI.
    • De lo contrario, el resultado es una variable, es decir, el campo I en la instancia de estructura dada por E.

No estoy seguro de por qué la parte del campo de instancia se refiere específicamente a los tipos de estructura, pero la parte del campo estático no lo hace. La parte importante es si la expresión se clasifica como una variable o un valor. Eso es entonces importante en la invocación de miembro de función ...

Sección 12.6.6.1 (Invocación de miembro de función, general)

El procesamiento en tiempo de ejecución de una invocación de miembro de función consta de los siguientes pasos, donde M es el miembro de función y, si M es un miembro de instancia, E es la expresión de instancia:

[...]

  • De lo contrario, si el tipo de E es un tipo de valor V, y M se declara o se invalida en V:
    • [...]
    • Si E no se clasifica como una variable, entonces se crea una variable local temporal del tipo de E y se asigna el valor de E a esa variable. E se reclasifica como una referencia a esa variable local temporal. La variable temporal es accesible como esta dentro de M, pero no de ninguna otra manera. Por lo tanto, solo cuando E es una variable verdadera es posible que la persona que llama observe los cambios que M hace a esto.

Aquí hay un ejemplo autocontenido:

using System; using System.Globalization; struct Counter { private int count; public int IncrementedCount => ++count; } class Test { static readonly Counter readOnlyCounter; static Counter readWriteCounter; static void Main() { Console.WriteLine(readOnlyCounter.IncrementedCount); // 1 Console.WriteLine(readOnlyCounter.IncrementedCount); // 1 Console.WriteLine(readOnlyCounter.IncrementedCount); // 1 Console.WriteLine(readWriteCounter.IncrementedCount); // 1 Console.WriteLine(readWriteCounter.IncrementedCount); // 2 Console.WriteLine(readWriteCounter.IncrementedCount); // 3 } }

Aquí está el IL para una llamada a readOnlyCounter.IncrementedCount :

ldsfld valuetype Counter Test::readOnlyCounter stloc.0 ldloca.s V_0 call instance int32 Counter::get_IncrementedCount()

Eso copia el valor del campo en la pila, luego llama a la propiedad ... para que el valor del campo no cambie; se está incrementando el count dentro de la copia.

Compare eso con la IL para el campo de lectura-escritura:

ldsflda valuetype Counter Test::readWriteCounter call instance int32 Counter::get_IncrementedCount()

Eso hace que la llamada se realice directamente en el campo, por lo que el valor del campo termina cambiando dentro de la propiedad.

Hacer una copia puede ser ineficiente cuando la estructura es grande y el miembro no la muta. Es por eso que en C # 7.2 y superior, el modificador de readonly se puede aplicar a una estructura. Aquí hay otro ejemplo:

using System; using System.Globalization; readonly struct ReadOnlyStruct { public void NoOp() {} } class Test { static readonly ReadOnlyStruct field1; static ReadOnlyStruct field2; static void Main() { field1.NoOp(); field2.NoOp(); } }

Con el modificador de readonly en la estructura en sí, la llamada field1.NoOp() no crea una copia. Si eliminas el modificador de readonly y recompilas, verás que crea una copia como lo hizo en readOnlyCounter.IncrementedCount .

Tengo una codeblog.jonskeet.uk/2014/07/16/… que escribí y descubrí que los campos de readonly estaban causando problemas de rendimiento en Noda Time. Afortunadamente, ahora está arreglado con el modificador de readonly en las estructuras en su lugar.