c# - studio - ¿Los tipos de enumeración de.NET son realmente tipos de valores mutables?
tipos de funciones en c# (1)
Mirando, con reflexión, en los campos de un tipo de enumeración, noté para mi sorpresa que el campo de instancia de "respaldo" que contiene el valor real de una instancia particular de la enumeración no es private
, como habría pensado, sino public
. Y tampoco fue de readonly
. ( IsPublic
verdadero, IsInitOnly
falso.)
Muchas personas consideran que los tipos de valores "cambiables" en el sistema de tipo .NET son "malvados", así que ¿ por qué los tipos de enumeración (tal como se crearon a partir del código C # por ejemplo) son solo eso?
Ahora, como resultado, el compilador de C # tiene algún tipo de magia que niega la existencia del campo de instancia pública (pero mira a continuación), pero en por ejemplo PowerShell puedes hacer esto:
prompt> $d = [DayOfWeek]::Thursday
prompt> $d
Thursday
prompt> $d.value__ = 6
prompt> $d
Saturday
El valor de value__
se puede escribir en.
Ahora, para hacer esto en C #, tuve que usar dynamic
porque parece que con el enlace de miembro de compilación normal, C # pretende que el campo de instancia public
no exista. Por supuesto, para usar la dynamic
, tendremos que usar el boxeo del valor enum.
Aquí hay un ejemplo de código C #:
// create a single box for all of this example
Enum box = DayOfWeek.Thursday;
// add box to a hash set
var hs = new HashSet<Enum> { box, };
// make a dynamic reference to the same box
dynamic boxDyn = box;
// see and modify the public instance field
Console.WriteLine(boxDyn.value__); // 4
boxDyn.value__ = 6;
Console.WriteLine(boxDyn.value__); // 6 now
// write out box
Console.WriteLine(box); // Saturday, not Thursday
// see if box can be found inside our hash set
Console.WriteLine(hs.Contains(box)); // False
// we know box is in there
Console.WriteLine(object.ReferenceEquals(hs.Single(), box)); // True
Creo que los comentarios hablan por sí mismos. Podemos mutar una instancia del tipo enum DayOfWeek
(podría ser cualquier tipo de enumeración de un ensamblaje BCL o de un ensamblaje "hecho en casa") a través de un campo public
. Dado que la instancia estaba en una tabla hash y la mutación conduce a un cambio de código hash, la instancia está en el "cubo" incorrecto después de la mutación, y el HashSet<>
no puede funcionar.
¿Por qué los diseñadores de .NET eligieron hacer público el campo de instancia de los tipos enum?
Permítanme tratar de dar sentido a esta pregunta algo confusa para los lectores que no están familiarizados con cómo se generan las enumeraciones detrás de escena. El código C #:
enum E { A, B }
se convierte en IL
.class private auto ansi sealed E extends [mscorlib]System.Enum
{
.field public specialname rtspecialname int32 value__
.field public static literal valuetype E A = int32(0x00000000)
.field public static literal valuetype E B = int32(0x00000001)
}
O, para volver a escribir eso en C # nuevamente, la enumeración es equivalente a la siguiente pseudo-C #:
struct E : System.Enum
{
public int value__;
public const E A = 0;
public const E B = 1;
}
La pregunta es: ¿por qué el valor del campo mágico value__
público?
No estaba cerca de esta decisión de diseño, así que tendría que hacer una conjetura. Mi suposición educada sería: ¿cómo inicializar una instancia de la estructura si el campo no es público?
Usted hace un constructor, al que debe llamar, y eso le está dando trabajo al jitter, y ¿cuánto le cuesta el costo de rendimiento de ese trabajo? Si la respuesta es "me compra el tiempo de ejecución previniéndome de hacer algo tonto y peligroso que no debería estar haciendo en primer lugar y tuve que trabajar muy duro para hacerlo en absoluto", entonces les presento que esto no es una relación costo-beneficio convincente.
Como la instancia estaba en una tabla hash y la mutación lleva a un cambio de código hash, la instancia está en el "cubo" incorrecto después de la mutación, y el HashSet no puede funcionar.
Eso es varias millas más allá de la línea "si duele cuando lo haces, entonces deja de hacer eso ".