c# - I tipi di enum.NET sono tipi di valori effettivamente mutabili?



powershell enums (1)

Guardando, con la riflessione, ai campi di un tipo enum, ho notato con mia sorpresa che il campo dell'istanza "backing" che contiene il valore reale di una particolare istanza dell'enumerazione non è private , come avrei pensato, ma public . E non era nemmeno di readonly . ( IsPublic true, IsInitOnly false.)

Molte persone considerano i tipi di valore "mutabili" nel sistema di tipo .NET "malvagio", quindi perché i tipi di enum (come creati dal codice C # per esempio) sono proprio così?

Ora, come risulta, il compilatore C # ha un qualche tipo di magia che nega l'esistenza del campo dell'istanza pubblica (ma vedi sotto), ma in PowerShell ad esempio puoi fare questo:

prompt> $d = [DayOfWeek]::Thursday
prompt> $d
Thursday
prompt> $d.value__ = 6
prompt> $d
Saturday

Il valore del value__ può essere scritto su.

Ora, per fare ciò in C #, ho dovuto usare dynamic perché sembra che con il normale binding dei membri in fase di compilazione, C # pretenda che il campo dell'istanza public non esista. Ovviamente per usare la dynamic , dovremo usare la boxe del valore enum.

Ecco un esempio di codice 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

Penso che i commenti parlino da soli. Possiamo mutare un'istanza del tipo enum DayOfWeek (potrebbe essere qualsiasi tipo di enum da un assembly BCL o da un assembly "fatto in casa") attraverso un campo public . Poiché l'istanza era in una tabella hash e la mutazione ha portato a una modifica del codice hash, l'istanza si trova nel "bucket" sbagliato dopo la mutazione e HashSet<> non può funzionare.

Perché i progettisti di .NET hanno scelto di rendere pubblico il campo istanza dei tipi enum?

https://src-bin.com


Answer #1

Lasciatemi provare a dare un senso a questa domanda piuttosto confusa per i lettori che non hanno familiarità con il modo in cui le enumerazioni vengono generate dietro le quinte. Il codice C #:

enum E { A, B }

diventa l'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)
} 

Oppure, per riscriverlo nuovamente in C #, l'enum è equivalente al seguente pseudo-C #:

struct E : System.Enum
{
    public int value__;
    public const E A = 0;
    public const E B = 1;
}

La domanda è: perché il valore del campo magico è pubblico?

Non ero in giro per questa decisione di progettazione, quindi dovrei fare una supposizione istruita. La mia ipotesi istruita sarebbe: come si inizializza un'istanza della struttura se il campo non è pubblico?

Costruisci un costruttore, che devi chiamare, e questo sta dando lavoro al jitter, e quali sono i costi delle prestazioni di quel lavoro che ti comprano? Se la risposta è "mi compra il runtime impedendomi di fare qualcosa di folle e pericoloso che non dovrei fare in primo luogo e ho dovuto lavorare davvero duramente per fare" quindi mi sottometto che questo non è un convincente rapporto costo-benefici.

Poiché l'istanza era in una tabella hash e la mutazione ha portato a una modifica del codice hash, l'istanza si trova nel "bucket" errato dopo la mutazione e HashSet non può funzionare.

Passano diverse miglia oltre "se fa male quando lo fai, poi smetti di fare quella " linea.





immutability