2010-09-16 15 views
27

Ich bin sicher, ich habe irgendwo gesehen, dass ich das folgende tun kann, indem ich ein Attribut über meiner Init() -Methode benutze, das dem Compiler sagt, dass die Init() -Methode nur vom Konstruktor aufgerufen werden muss Feld festgelegt werden. Ich habe vergessen, wie das Attribut heißt, und ich kann es nicht auf Google finden.Wie lege ich ein readonly-Feld in einer initialize-Methode fest, die vom Konstruktor aufgerufen wird?

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

Antwort

33

Rob's answer ist der Weg, es zu tun, in meinem Buch. Wenn Sie mehrere Felder initialisieren möchten, können Sie es tun out Parameter:

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

Persönlich finde ich dieses Gefühl in bestimmten Szenarien macht wie, wenn Sie Ihre Felder readonly zu sein, aber Sie auch sein wollen in der Lage, sie in einer abgeleiteten Klasse anders zu setzen (ohne eine Tonne von Parametern durch irgendeinen protected Konstruktor zu verketten). Aber vielleicht bin ich es nur.

+2

out Parameter, yuk! –

+1

@Chuck: Hey, ich habe nie gesagt, dass es schön ist. –

+0

Ich löste es mit Reflection. Siehe unten .. – Derar

6

Dies ist nicht möglich. Felder, die mit readonly markiert sind, können nur vom Konstruktor

+0

Nicht, dass es völlig falsch ist, aber diese Antwort verblassen wirklich im Vergleich zu den anderen Antworten hier. Eine einfache Antwort "Kann nicht gemacht werden" lässt viel zu wünschen übrig. Vielleicht war das eine der ersten Antworten? – Assimilater

1

C# Compiler Sie nur erlaubt eingestellt werden Nur-Lese-Felder zu setzen, wenn Sie sie inline sind initialisiert:

private readonly int readonlyField = 1; 

oder aus dem Konstruktor:

public Class() 
{ 
    readonlyField = 1; 
} 
9

die einzige Lösung, die ich denken kann, ist der Wert aus dem Init() Methode zurückzugeben, die die readonly Feld zugeordnet werden soll:

public class Class 
{ 
    private readonly int readonlyField; 

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

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

Ich war mir sicher, dass ich das geschafft habe. Mein Gehirn muss mir Streiche spielen. Danke an alle –

+0

@BenAnderson, ich weiß, was du meinst - Ich verbrachte Stunden vor ein paar Tagen Traweln des 'net, wie ich sicher war, dass ich irgendwo gelesen habe, dass SMB2 eine Dateikopie von einem Remote-Server zu einem anderen nicht geroutet werden kann über die Client-Maschine, die die Anfrage macht. Stunden verloren und ich konnte keine Beweise finden, obwohl ich * unnachgiebig war * Ich würde lesen, dass es möglich war! : S – Rob

14

Nein, können Sie nicht. Grundsätzlich sollten Sie die Initialisierung im Konstruktor durchführen - dafür ist es da.

Wenn Sie mehrere Konstruktoren verwenden, versuchen Sie zu Wiederverwendungszwecken, alle bis auf eine Kette zu einem "Master" -Konstruktor zu machen, der die eigentliche Arbeit erledigt.

Wenn einer der Werte eine komplexe Berechnung erfordert, ist es möglicherweise nicht angemessen, im Konstruktor trotzdem berechnet zu werden - und wenn ja, können Sie den Berechnungsteil in eine private Methode setzen, die dann den Wert zurückgibt Rufen Sie diese Methode vom Konstruktor auf.

EDIT: Ich würde Reflexion nicht als eine echte Möglichkeit, dies zu tun. Es gibt alle Arten von Aussagen, die Sie machen können, die Missbrauch durch Reflexion nicht enthalten. Soll das String-Literal "x" in "y" umgewandelt werden? Sicher, das kannst du mit Nachdenken tun, wenn du die richtigen Berechtigungen hast ... aber das solltest du absolut nicht.

+0

kann über Reflection gelöst werden. Siehe unten das Beispiel. – Derar

4

Jared ist richtig; das ist nicht möglich. Die Problemumgehungen, die ich denken kann, sind:

  1. initialisieren Sie das Feld in der Deklaration.
  2. Initialisieren Sie das Feld im Konstruktor (Manuell inline Ihre Init Methode).
  3. , das Feld auf einen Wert zuweisen durch ein Verfahren zurückgeführt, z.B .: _myField = GetInitialMyFieldValue();
  4. Geben das Feld dem Init Verfahren, mit dem out Modifikator.Dies kann nützlich sein, wenn Sie viele zu initialisierende Felder haben, die von Konstruktorparametern abhängig sind. Z.B.

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

Ich denke, es Verwendung Reflexion funktioniert, wenn. Eigentlich funktioniert das für mich:

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

und

static void Main(string[] args) 
     { 

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

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

das ist 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;   
     } 

die einzige Sache ist, dass readonlyField Öffentlichkeit ist nicht privat. Ich weiß, dass Sie ein privates Feld bearbeiten können, aber ich bin mir nicht sicher, warum es nicht für mich funktioniert!

+0

funktioniert jetzt auf Private Member-Variablen .. – Derar

+10

Nur weil Sie * können * dies tun bedeutet nicht, dass Sie * sollten * ... und natürlich in vielen Situationen haben Sie keine Erlaubnis, dies zu tun. –

+0

Ich denke nicht, dass das für die Reflexion gedacht war. Unnötige Performance-Hit ... – clyc

12

Anstatt eine Initialize-Methode zu verwenden, können Sie einen Basiskonstruktor über alle anderen Konstruktoren erben. heißt

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

Dies ist ein wenig zu spät sein kann, die ursprüngliche Person in Not zu erreichen, aber es scheint, wie dies sauber das Problem lösen ... Ohne die Notwendigkeit, jede Art von fieser Reflexion oder out-Parameter zu verwenden.

+0

Dies ist definitiv eine klügere Möglichkeit, Code zwischen Konstruktoren zu teilen. – MikeMurko