property español c# performance readonly immutability

español - readonly textbox c#



¿Hay alguna sobrecarga en el tiempo de ejecución para leer solo? (4)

Hay que verlo desde el mismo punto de vista que los modificadores de acceso. Los modificadores de acceso existen en IL, pero ¿son realmente una verificación en tiempo de ejecución? (1) No puedo asignar campos privados directamente en tiempo de compilación, (2) Puedo asignarlos usando la reflexión. Hasta ahora parece que no hay verificación de tiempo de ejecución, como readonly .

Pero examinemos los modificadores de acceso. Haz lo siguiente:

  1. Crear Asamblea A.dll con clase pública C
  2. Cree un ensamblado B.exe que haga referencia a A.dll. B.exe usa la clase C.
  3. Construye los dos conjuntos. Ejecutar B.exe funciona bien.
  4. Reconstruye A.dll pero establece la clase C en interno. Reemplace A.dll en el directorio de B.exe.

Ahora, ejecutar B.exe lanza una excepción de tiempo de ejecución.

Los modificadores de acceso también existen en IL, ¿verdad? Entonces, ¿cuál es su propósito? El propósito es que otros conjuntos que hacen referencia a un conjunto .Net necesitan saber a qué se les permite acceder y a qué no se les permite acceder, tanto el tiempo de compilación como el tiempo de ejecución.

Readonly parece tener un propósito similar en IL . Le dice a otros ensamblados si pueden escribir en un campo en un tipo particular. Sin embargo, readonly no parece tener la misma comprobación en tiempo de ejecución que muestran los modificadores de acceso en mi muestra anterior. Parece que readonly es una comprobación en tiempo de compilación y no se produce en tiempo de ejecución. Eche un vistazo a una muestra de rendimiento aquí: Rendimiento de solo lectura vs const .

De nuevo, esto no significa que la IL sea inútil. La IL se asegura de que se produzca un error en tiempo de compilación en primer lugar. Recuerda, cuando construyes no construyes contra código, sino ensamblajes.

Por alguna razón, siempre he asumido que los campos de readonly tienen una sobrecarga asociada a ellos, lo que consideré como el CLR que realiza un seguimiento de si un campo de readonly se ha inicializado o no. La sobrecarga aquí sería un uso adicional de memoria para realizar un seguimiento del estado y una verificación al asignar un valor.

Quizás asumí esto porque no sabía que un campo de solo readonly solo podía inicializarse dentro de un constructor o dentro de la declaración de campo y sin una verificación en tiempo de ejecución, no podría garantizar que no se asigne varias veces en varios métodos. Pero ahora lo sé, el compilador de C # podría verificarlo de forma estática, ¿no? Entonces, ¿ese es el caso?

Otra razón es que he leído que el uso de readonly tiene un ''leve'' impacto en el rendimiento, pero nunca se incluyeron en esta afirmación y no puedo encontrar información sobre este tema, de ahí mi pregunta. No sé qué otro impacto en el rendimiento podría haber aparte de las verificaciones en tiempo de ejecución.

Una tercera razón es que vi que readonly se conserva en el IL compilado como initonly , entonces, ¿cuál es la razón para que esta información esté en el IL si readonly no es más que una garantía por parte del compilador de C # de que el campo nunca se asigna a ¿Fuera de un constructor o declaración?

Por otro lado, he descubierto que puede establecer el valor de un readonly int través de la reflexión sin que el CLR lance una excepción, lo que no debería ser posible si readonly era una verificación en tiempo de ejecución.

Así que mi conjetura es: la ''readonlyness'' es solo una característica de compilación, ¿alguien puede confirmar / negar esto? Y si lo es, ¿cuál es la razón para que esta información se incluya en el IL?


Incluso si la lectura solo tuviera efecto en el momento de la compilación, aún sería necesario almacenar los datos en el ensamblaje (es decir, el IL). El CLR es un Common Language Runtime: las clases escritas en un idioma pueden ser utilizadas y extendidas por otros idiomas.

Dado que cada compilador para el CLR no sabrá cómo leer y compilar todos los demás idiomas, a fin de preservar la semántica de los campos de readonly , esos datos deben almacenarse en el ensamblaje para que los compiladores de otros idiomas lo respeten.

Por supuesto, el hecho de que el campo esté marcado como readonly significa que el JIT puede hacer otras cosas, como la optimización (por ejemplo, usos en línea del valor), etc. Independientemente del hecho de que usó la reflexión para cambiar el valor del campo, creando IL que modifica un campo de initonly fuera del constructor correspondiente (instancia o estática, dependiendo del tipo de campo), dará como resultado un ensamblaje no verificable.


Si está utilizando una variable de instancia estándar, readonly tendrá un rendimiento casi idéntico a una variable normal. El IL agregado se convierte en una verificación de tiempo de compilación, pero se ignora en el tiempo de ejecución.

Si estás usando un miembro de solo lectura estática, las cosas son un poco diferentes ...

Dado que el miembro de solo lectura estática se establece durante el constructor estático, el JIT "sabe" que existe un valor. No hay memoria adicional, solo la lectura evita que otros métodos configuren esto, pero eso es una verificación de tiempo de compilación.

Una vez que el JIT sabe que este miembro nunca puede cambiar, se "codifica" en el tiempo de ejecución, por lo que el efecto final es como tener un valor constante. La diferencia es que tomará más tiempo durante el tiempo de JIT, ya que el compilador JIT necesita hacer un trabajo extra para conectar el valor de solo lectura en su lugar. (Esto va a ser muy rápido, sin embargo.)

Expert C ++ / CLI por Marcus Hegee tiene una explicación razonablemente buena de esto.


Un punto importante que aún no se menciona en ninguna otra respuesta es que cuando se accede a un campo de solo lectura o cuando se accede a alguna propiedad, la solicitud se satisface utilizando una copia de los datos. Si los datos en cuestión son un tipo de valor con más de 4-8 bytes de datos, el costo de esta copia adicional a veces puede ser significativo. Tenga en cuenta que si bien hay un gran aumento en el costo cuando las estructuras crecen de 16 bytes a 17, las estructuras pueden ser un poco más grandes y aún más rápidas que las clases en muchas aplicaciones, si no se copian con demasiada frecuencia . Por ejemplo, si se supone que uno tiene un tipo que representa los vértices de un triángulo en el espacio tridimensional. Una implementación directa sería una estructura que contiene una estructura con tres float para cada punto; probablemente 36 bytes en total. Si los puntos, y las coordenadas dentro de cada punto, son campos públicos mutables, uno puede acceder a someTriangle.P1.X rápida y sencilla, sin tener que copiar ningún dato excepto la coordenada Y del vértice 1. Por otro lado, si P1 fue una propiedad o un campo de readonly , el compilador tendría que copiar P1 a una estructura temporal, y luego leer X de eso.