know - string empty asp net c#
Se modificó el comportamiento de string.Empty(o System.String:: Empty) en.NET 4.5 (3)
Version corta:
El código de C #
typeof(string).GetField("Empty").SetValue(null, "Hello world!");
Console.WriteLine(string.Empty);
cuando se compila y se ejecuta, da salida "Hello world!"
en .NET versión 4.0 y anterior, pero da ""
en .NET 4.5 y .NET 4.5.1.
¿Cómo se puede ignorar una escritura en un campo así o quién restablece este campo?
Versión más larga:
Nunca he entendido realmente por qué el campo string.Empty
(también conocido como [mscorlib]System.String::Empty
) no es const
(también conocido como literal
), consulte " ¿Por qué no String.Empty es una constante? ". Esto significa que, por ejemplo, en C # no podemos usar string.Empty
en las siguientes situaciones:
- En una instrucción
switch
en el formulario decase string.Empty:
- Como el valor predeterminado de un parámetro opcional, como
void M(string x = string.Empty) { }
- Al aplicar un atributo, como
[SomeAttribute(string.Empty)]
- Otras situaciones donde se requiere una constante de tiempo de compilación
que tiene implicaciones para la bien conocida "guerra religiosa" sobre si usar string.Empty
o ""
, ver " In C #, ¿debería usar string.Empty o String.Empty o" "para inicializar una cadena? ".
Hace un par de años me divertí al configurar Empty
en alguna otra instancia de cadena mediante la reflexión, y ver cuántas partes del BCL comenzaron a comportarse de forma extraña debido a eso. Fueron muchos. Y el cambio de la referencia Empty
pareció persistir durante toda la vida de la aplicación. Ahora, el otro día traté de repetir ese pequeño truco, pero luego usé una máquina .NET 4.5, y no pude hacerlo más.
(Nota: si tiene .NET 4.5 en su máquina, probablemente su PowerShell
todavía use una versión anterior de .NET, intente copiar y pegar [String].GetField("Empty").SetValue($null, "Hello world!")
en PowerShell para ver algunos efectos del cambio de esta referencia).
Cuando traté de buscar una razón para esto, tropecé con el interesante hilo " ¿Cuál es la causa de este FatalExecutionEngineError en .NET 4.5 beta? ". En la respuesta aceptada a esa pregunta, se observa que a través de la versión 4.0, System.String
tenía un constructor estático .cctor
en el que se establecía el campo Empty
(en la fuente C #, que probablemente solo sería un inicializador de campo, por supuesto) , mientras que en 4.5 no existe ningún constructor estático. En ambas versiones, el campo en sí tiene el mismo aspecto:
.field public static initonly string Empty
(como se vio con IL DASM).
No hay otros campos que String::Empty
que se vean afectados. Como ejemplo, experimenté con System.Diagnostics.Debugger::DefaultCategory
. Este caso parece análogo: una clase sellada que contiene un campo static readonly
( static initonly
) de tipo string
. Pero en este caso, funciona bien cambiar el valor (referencia) a través de la reflexión.
Volver a la pregunta:
¿Cómo es posible, técnicamente, que Empty
no parezca cambiar (en 4.5) cuando configuro el campo? He verificado que el compilador de C # no "engaña" con la lectura, emite IL como:
ldsfld string [mscorlib]System.String::Empty
entonces el campo real debería ser leído.
Editar después de que la bonanza se haya puesto en mi pregunta: initonly
en cuenta que la operación de escritura (que necesita una reflexión segura, ya que el campo es de readonly
(también initonly
como initonly
en IL)) realmente funciona como se esperaba. Es la operación de lectura que es anómala. Si lee con reflexión, como en typeof(string).GetField("Empty").GetValue(null)
, todo es normal (es decir, se ve el cambio de valor). Ver comentarios a continuación.
Entonces, la mejor pregunta es: ¿por qué esta nueva versión del marco engaña cuando lee este campo en particular?
La diferencia radica en el JIT para la nueva versión de .NET, que aparentemente optimiza las referencias a String.Empty
al String.Empty
una referencia a una instancia de String
particular en lugar de cargar el valor almacenado en el campo Empty
. Esto se justifica bajo la definición de la restricción de solo inicio en ECMA-335 Partición I §8.6.1.2, que puede interpretarse como que significa que el valor del campo String.Empty
no cambiará después de que se inicialice la clase String
.
No tengo una respuesta, juste alguna pista, tal vez.
La única diferencia que veo entre String::Empty
y System.Diagnostics.Debugger::DefaultCategory
es la primera que está etiquetada con __DynamicallyInvokableAttribute
.
No sé el significado de este atributo no documentado. Se ha formulado una pregunta sobre este atributo en SO: ¿Para qué sirve el atributo __DynamicallyInvokable?
¿Solo puedo suponer que este atributo es capturado por el tiempo de ejecución para hacer algo de almacenamiento en caché?
Porque puede.
El valor de estos campos de initonly
definidos por el initonly
son invariantes globales para el tiempo de ejecución de .NET. Si estos invariantes se rompen, ya no hay garantías de ningún tipo con respecto al comportamiento.
En C ++, probablemente tendremos una regla que indique que esto causa un comportamiento indefinido . En .NET, también es un comportamiento indefinido, simplemente por la ausencia de una regla que diga lo que sucede cuando System.String.Empty.Length > 0
. La especificación completa de todas las capas de .NET y C # describe el comportamiento cuando System.String.Empty.Length == 0
y un conjunto completo de invariantes también se mantienen.
Para obtener más información sobre las optimizaciones que varían entre los tiempos de ejecución y las implicaciones, consulte las respuestas a
- ¿Cuáles son las implicaciones de pedir Reflection APIs para sobrescribir System.String.Empty?