Posso cambiare un campo privato di sola lettura in C # usando la riflessione?

Mi chiedo, dato che molte cose possono essere fatte usando il reflection, posso cambiare un campo readonly privato dopo che il costruttore ha completato la sua esecuzione?
(nota: solo curiosità)

public class Foo { private readonly int bar; public Foo(int num) { bar = num; } public int GetBar() { return bar; } } Foo foo = new Foo(123); Console.WriteLine(foo.GetBar()); // display 123 // reflection code here... Console.WriteLine(foo.GetBar()); // display 456 

    Puoi:

     typeof(Foo) .GetField("bar",BindingFlags.Instance|BindingFlags.NonPublic) .SetValue(foo,567); 

    La cosa ovvia è provarlo:

     using System; using System.Reflection; public class Test { private readonly string foo = "Foo"; public static void Main() { Test test = new Test(); FieldInfo field = typeof(Test).GetField ("foo", BindingFlags.Instance | BindingFlags.NonPublic); field.SetValue(test, "Hello"); Console.WriteLine(test.foo); } } 

    Funziona bene (Java ha regole diverse, interessante – devi impostare esplicitamente il Field per essere accessibile, e funzionerà comunque solo per i campi di istanza.)

    Sono d’accordo con le altre risposte in quanto funziona in generale e in particolare con il commento di E. Lippert che questo non è un comportamento documentato e quindi non un codice a prova di futuro.

    Tuttavia, abbiamo notato anche un altro problema. Se stai eseguendo il tuo codice in un ambiente con autorizzazioni limitate potresti ottenere un’eccezione.

    Abbiamo appena avuto un caso in cui il nostro codice ha funzionato correttamente sulle nostre macchine, ma abbiamo ricevuto una VerificationException quando il codice è stato eseguito in un ambiente limitato. Il colpevole era una chiamata di riflessione al setter di un campo di sola lettura. Ha funzionato quando abbiamo rimosso la restrizione di sola lettura di quel campo.

    Hai chiesto perché vorresti rompere l’incapsulamento in quel modo.

    Io uso una class helper di entity framework per idratare le quadro. Questo usa il reflection per ottenere tutte le proprietà di una nuova quadro vuota, e corrisponde alla proprietà / nome del campo alla colonna nel gruppo di risultati, e lo imposta usando propertyinfo.setvalue ().

    Non voglio che qualcun altro sia in grado di modificare il valore, ma non voglio fare tutto il ansible per i metodi di idratazione del codice personalizzato per ogni quadro.

    I miei molti stored process restituiscono i set di risultati che non corrispondono direttamente alle tabelle o alle viste, quindi gli ORM del codice non fanno nulla per me.

    La risposta è sì, ma soprattutto:

    Perché vorresti? Spezzare intenzionalmente l’incapsulamento mi sembra un’idea terribilmente brutta.

    Usare la riflessione per cambiare un campo di sola lettura o costante è come combinare la legge delle conseguenze non intenzionali con la legge di Murphy .

    Non farlo.

    Ho appena passato un giorno a sistemare un bug surreale in cui gli oggetti potrebbero non essere di loro tipo dichiarato.

    La modifica del campo di sola lettura ha funzionato una volta. Ma se provassi a modificarlo di nuovo, otterrai situazioni come questa:

     SoundDef mySound = Reflection_Modified_Readonly_SoundDef_Field; if( !(mySound is SoundDef) ) Log("Welcome to impossible-land!"); //This would run 

    Quindi non farlo.

    Questo era nel runtime Mono (Unity game engine).

    Un altro modo semplice per farlo usando unsafe (o potresti passare il campo ad un metodo C tramite DLLImport e impostarlo lì).

     using System; namespace TestReadOnly { class Program { private readonly int i; public Program() { i = 66; } private unsafe void ForceSet() { fixed (int* ptr = &i) *ptr = 123; } static void Main(string[] args) { var program = new Program(); Console.WriteLine("Contructed Value: " + program.i); program.ForceSet(); Console.WriteLine("Forced Value: " + program.i); } } } 

    Voglio solo aggiungere che se hai bisogno di fare queste cose per il test delle unità, puoi usare:

    A) La class PrivateObject

    B) Avrai ancora bisogno di un’istanza di PrivateObject, ma puoi creare oggetti “Accessor” con Visual Studio. Procedura: rigenerare gli accessorati privati

    Se si impostano campi privati ​​di un object nel codice al di fuori del test delle unità, si tratterebbe di un’istanza di “odore di codice”, penso che l’unica altra ragione per cui si vorrebbe farlo è se si ha a che fare con una terza parte libreria e non è ansible modificare il codice della class di destinazione. Anche allora, probabilmente vorrai contattare la terza parte, spiegare la tua situazione e vedere se non andranno avanti e cambiare il loro codice per soddisfare le tue necessità.