Domanda

Mi chiedo, dal momento che un sacco di cose può essere fatto utilizzando la riflessione, posso cambiare un privato readonly campo dopo che il costruttore ha completato la sua esecuzione?
(nota:solo una 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
È stato utile?

Soluzione

È possibile:

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

Altri suggerimenti

La cosa più ovvia è quella di provare:

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);
    }        
}

Questo funziona bene. (Java ha regole diverse, è interessante notare -. È necessario impostare in modo esplicito il Field per essere accessibili, e funziona solo per i campi di istanza in ogni caso)

Sono d'accordo con le altre risposte in quanto funziona in generale e soprattutto con il commento di E. Lippert che ciò non sia documentato il comportamento e il codice quindi non a prova di futuro.

Tuttavia, abbiamo anche notato un altro problema. Se si sta eseguendo il codice in un ambiente con autorizzazioni limitate si potrebbe ottenere una deroga.

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

Hai chiesto perché si vuole rompere l'incapsulamento del genere.

Io uso una classe di entità di supporto per idratare entità. Questo utilizza la riflessione per ottenere tutte le proprietà di una nuova entità vuota, e corrisponde al nome di proprietà / campo per la colonna nel set di risultati, e impostare sta utilizzando propertyinfo.setvalue ().

Non voglio che nessun altro sia in grado di modificare il valore, ma io non voglio prendere tutto lo sforzo per i metodi di idratazione di codice personalizzato per ogni entità sia.

Il mio molti dei miei stored procedure di risultati che non corrispondono direttamente alle tabelle o viste ritorno, in modo che il codice di GEN ORM fare nulla per me.

La risposta è sì, ma ancora più importante:

Perché si vuole a? Intenzionalmente rottura incapsulamento sembra una orribilmente cattiva idea per me.

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

Non fare questo.

Ho appena trascorso una giornata fissazione di un bug surreale in cui gli oggetti potrebbero essere non del proprio tipo dichiarato.

La modifica del campo di sola lettura ha funzionato una volta. Ma se si è tentato di modificare di nuovo, si otterrebbe 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 è stato il runtime Mono (Unità motore di gioco).

Un altro modo semplice per fare questo usando non sicuro (o si potrebbe passare il campo per un metodo di 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 avete bisogno di fare questa roba per i test di unità, quindi è possibile utilizzare:

A) Il PrivateObject classe

B) Si avrà bisogno di una PrivateObject esempio, ma in grado di generare "funzione di accesso" oggetti di Visual Studio. Come:Rigenerare Le Funzioni Di Accesso Privato

Se si sta impostando i campi privati di un oggetto nel codice al di fuori di test di unità, che sarebbe un esempio di "codice odore" penso che forse l'unico altro motivo che si desidera fare questo se avete a che fare con una libreria di terze parti, e non è possibile modificare la destinazione codice di classe.Anche allora, probabilmente si desidera contattare il 3 ° partito, spiega la tua situazione e vedere se non andare avanti e cambiare il loro codice a soddisfare le vostre necessità.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top