2010-09-16 13 views
27

Sono sicuro di aver visto da qualche parte che posso fare quanto segue usando un attributo sopra il mio metodo Init(), che dice al compilatore che il metodo Init() deve essere chiamato solo dal costruttore, permettendo così il readonly campo da impostare. Ho dimenticato però come viene chiamato l'attributo e non riesco a trovarlo su google.Come si imposta un campo di sola lettura in un metodo di inizializzazione che viene chiamato dal costruttore?

public class Class 
{ 
    private readonly int readonlyField; 

    public Class() 
    { 
     Init(); 
    } 

    // Attribute here that tells the compiler that this method must be called only from a constructor 
    private void Init() 
    { 
     readonlyField = 1; 
    } 
} 

risposta

33

Rob's answer è il modo per farlo, nel mio libro. Se è necessario inizializzare più campi è possibile farlo utilizzando out parametri:

public class Class 
{ 
    private readonly int readonlyField1; 
    private readonly int readonlyField2; 

    public Class() 
    { 
     Init(out readonlyField1, out readonlyField2); 
    } 

    protected virtual void Init(out int field1, out int field2) 
    { 
     field1 = 1; 
     field2 = 2; 
    } 
} 

Personalmente trovo questo ha un senso in determinati scenari, ad esempio quando si desidera che i campi siano readonly ma anche voler essere in grado di impostarli in modo diverso in una classe derivata (senza dover concatenare una tonnellata di parametri tramite il costruttore protected). Ma forse sono solo io.

+2

parametri out, yuk! –

+1

@Chuck: Hey, non ho mai detto che fosse carina. –

+0

L'ho risolto usando Reflection. Vedi sotto .. – Derar

6

Questo non può essere fatto. I campi che sono contrassegnati con readonly possono essere impostate solo dal costruttore

+0

Non che sia completamente sbagliato, ma questa risposta è davvero pallida rispetto alle altre risposte qui intorno. Un semplice "non può essere fatto" una risposta a frase lascia molto a desiderare. Forse questa è stata una delle prime risposte però? – Assimilater

1

compilatore C# consente solo di impostare i campi di sola lettura, se li stai inizializzazione in linea:

private readonly int readonlyField = 1; 

o dal costruttore:

public Class() 
{ 
    readonlyField = 1; 
} 
9

l'unica soluzione che viene in mente è quello di restituire il valore dal metodo Init() che il campo readonly necessario assegnare:

public class Class 
{ 
    private readonly int readonlyField; 

    public Class() 
    { 
     readonlyField = Init(); 
    } 

    private int Init() 
    { 
     return 1; 
    } 
} 
+0

Ero sicuro di averlo fatto. Il mio cervello deve giocarmi brutti scherzi. Grazie a tutti –

+0

@BenAnderson, so cosa intendi: ho passato ore qualche giorno fa a trascinare la rete perché ero sicuro di aver letto da qualche parte che SMB2 consente una copia di file da un server remoto a un altro per non essere indirizzati tramite la macchina client che effettua la richiesta. Ore perse e non ho trovato nessuna prova, anche se ero * irremovibile * Avevo letto che era possibile! : S – Rob

14

No, non puoi. Fondamentalmente dovresti eseguire l'inizializzazione nel costruttore - ecco a cosa serve.

Ai fini del riutilizzo, se si dispone di più costruttori, provare a far rientrare tutti tranne uno di essi in un costruttore "principale" che esegue tutto il lavoro reale.

Se uno dei valori richiede calcolo complesso, allora non può essere opportuno essere calcolato nel costruttore in ogni caso - e se lo è, si può mettere la parte di calcolo in un metodo privato che ritorna il valore, quindi chiama quel metodo dal costruttore.

EDIT: Non includerei la riflessione come un vero modo di farlo. Ci sono tutti i tipi di dichiarazioni che puoi fare e che non includono gli abusi attraverso la riflessione. Vuoi la stringa letterale "x" da trasformare in "y"? Certo, puoi farlo con la riflessione, se hai i permessi giusti ... ma assolutamente non dovresti.

+0

può essere risolto tramite Reflection. Vedi sotto l'esempio. – Derar

4

Jared ha ragione; non è possibile. Le soluzioni alternative che posso pensare sono:

  1. Inizializzare il campo nella dichiarazione.
  2. Inizializza il campo nel costruttore (Inserisci manualmente il tuo metodo Init).
  3. assegnare il campo a un valore restituito da un metodo, ad es .: _myField = GetInitialMyFieldValue();
  4. Passare il campo per il metodo Init, con il modificatore out.Questo può essere utile se hai molti campi da inizializzare, che dipendono dai parametri del costruttore. Per esempio.

private readonly int _x; 
private readonly string _y; 

private void Init(int someConstructorParam, out int x, out string y){ .. } 

public Class(int someConstructorParam) 
{ 
    Init(someConstructorParam, out _x, out _y); 
} 
0

penso che funziona se l'uso di riflessione. In realtà questo funziona per me:

public class Class 
     { 
      private readonly int readonlyField; 
     public int MyField() 
     { 
      return readonlyField; 
     } 
     public Class() 
     { 
      readonlyField = 9; 
     } 
    } 

e

static void Main(string[] args) 
     { 

      Class classObj = new Class(); 
      Console.WriteLine(classObj.MyField());//9 

      Misc.SetVariableyByName(classObj, "readonlyField", 20);//20 
      Console.WriteLine(classObj.MyField()); 
     } 

questo è SetVariableByName():

public static b 

ool SetVariableyByName(object obj, string var_name, object value) 
      { 
       FieldInfo info = obj.GetType().GetField(var_name, BindingFlags.NonPublic| BindingFlags.Instance); 
       if (info == null) 
       return false; 
      /* ELSE */ 
      info.SetValue(obj, value); 
      return true;   
     } 

l'unica cosa è che non è readonlyField pubblico-privato. So che puoi modificare un campo privato, ma non sono sicuro del motivo per cui non funziona per me!

+0

ora funziona su variabili membro private. – Derar

+10

Solo perché * puoi * farlo in questo modo non significa che tu * dovresti * ... e ovviamente in molte situazioni non avrai il permesso di farlo. –

+0

Non penso che questo sia il significato della riflessione. Colpo di prestazione non necessario ... – clyc

12

Invece di utilizzare un metodo di inizializzazione, come fare per ereditare un costruttore di base attraverso tutti gli altri costruttori. cioè

public class MyClass 
{ 
    readonly int field1; 
    readonly double field2; 
    public MyClass(int field1, double field2) 
    { 
     //put whatever initialization logic you need here... 
     field1 = 10; 
     field2 = 30.2; 
    } 
    public MyClass(int field1, double field2p1, double field2p2) 
     : this(field1, (field2p1 + field2p2)) 
    { 
     //put anything extra in here 
    } 
} 

Questo può essere un po 'tardi per raggiungere la persona originale nel bisogno, ma sembra che questo modo pulito risolvere il problema ... senza la necessità di utilizzare qualsiasi tipo di brutto riflessione o fuori i parametri.

+0

Questo è sicuramente un modo più intelligente di condividere il codice tra costruttori. – MikeMurko

Problemi correlati