2009-04-27 10 views
8

so, Objektinitialisierer sind alle Arten von praktisch - vor allem, wenn Sie Linq tun, wo sie sind geradezu notwendig - aber ich kann nicht ganz diese eine heraus:Nested Verwendung von C# Objektinitialisierer

public class Class1 { 
    public Class2 instance; 
} 

public class Class2 { 
    public Class1 parent; 
} 

mit wie folgt aus:

Class1 class1 = new Class1(); 
class1.instance = new Class2(); 
class1.parent = class1; 

als Initialisierer:

Class1 class1 = new Class1() { 
    instance = new Class2() { 
     parent = class1 
    } 
}; 

dies nicht funktioniert, class1 ist angeblich eine nicht zugewiesene lokale Variable. es wird noch schwieriger, in Linq, wenn Sie so etwas wie

select new Class1() { ... 

es hat zu tun sind nicht einmal einen Namen beziehen durch!

Wie kann ich das umgehen? kann ich einfach verschachtelte Referenzen nicht mit Objektinitialisatoren machen?

Antwort

6

kann ich einfach nicht verschachtelte Referenzen machen Objektinitialisierer verwenden?

Sie haben Recht - Sie können nicht. Es würde einen Zyklus geben; A benötigt B für die Initialisierung, aber B benötigt A vor. Um genau zu sein - Sie können natürlich verschachtelte Objektinitialisierer machen, aber nicht mit zirkulären Abhängigkeiten.

Aber Sie können - und ich würde vorschlagen, Sie sollten wenn möglich - wie folgt arbeiten.

public class A 
{ 
    public B Child 
    { 
     get { return this.child; } 
     set 
     { 
     if (this.child != value) 
     { 
      this.child = value; 
      this.child.Parent = this; 
     } 
     } 
    } 
    private B child = null; 
} 

public class B 
{ 
    public A Parent 
    { 
     get { return this.parent; } 
     set 
     { 
     if (this.parent != value) 
     { 
      this.parent = value; 
      this.parent.Child = this; 
     } 
     } 
    } 
    private A parent = null; 
} 

Aufbau der Beziehung innerhalb des Grundstücks hat die benifit, dass Sie nicht einen inconsitent Zustand erhalten, wenn Sie eine der Initialisierungsanweisungen vergessen. Es ist ziemlich offensichtlich, dass dies eine suboptimale Lösung ist, weil Sie zwei Anweisungen benötigen, um eine Sache zu erledigen.

b.Parent = a; 
a.Child = b; 

Mit der Logik in den Eigenschaften erhalten Sie die Sache mit nur einer Aussage.

a.Child = b; 

Oder andersherum.

b.Parent = a; 

Und schließlich mit Objektinitialisierersyntax.

A a = new A { Child = new B() }; 
+0

Ich mag diese Antwort am besten, aber es funktioniert nicht für mich arbeiten, weil ich links heraus, dass Class2 ist eigentlich Liste , um die Beispiele klarer. Drat, es ging nach hinten los. –

1

Dieses Problem ist nicht spezifisch für Objektinitialisierer, es ist eine allgemeine Einschränkung in der C# -Sprache. Sie können eine lokale Variable erst verwenden, wenn sie definitiv zugewiesen wurde. Hier ist ein einfacher Repro

Class1 Foo(Class1 c1) { 
    return c1; 
} 

void Example() { 
    Class1 c1 = Foo(c1); 
} 
1

Ich glaube nicht, es trotzdem, dies zu umgehen ist, wenn die Anwendung des Class1 Objekt instanziieren muss es zuerst das Class2 Objekt geschaffen haben, so dass es weiß, in Speicher, in dem die Referenz befindet. Sie können das sehen, wenn Sie versuchen, die folgenden:

 Class1 myClass1 = null; 

     myClass1 = new Class1() 
     { 
      instance = new Class2 
      { 
       parent = myClass1 
      } 
     }; 

Dies wird kompilieren und ausführen, aber die übergeordnete Eigenschaft von Class2 wird null sein, da das ist, was der Wert zum Zeitpunkt der innere Codezeile ausgeführt wurde, was bedeutet, dass die innerste Linie die erste war, die ausgeführt wurde.

0

Sie können sicherlich verschachtelte Objektinitialisierer verwenden, ich mache es die ganze Zeit.

In Ihrem speziellen Fall haben Ihre Objekte jedoch einen Zirkelbezug. Sie müssen die Instanz instantiieren, bevor Sie sie der anderen zuweisen. Andernfalls übergeben Sie dem Compiler einen klassischen chicken and egg problem, den es nicht verarbeiten kann.

+0

es ist kein Huhn und Ei Problem, zumindest nicht, wie ich Initialisierer verstehe: nur syntaktischer Zucker zum Deklarieren eines Objekts und Festlegen seiner Eigenschaften. –

+0

Nun, Ihre "nicht zugewiesene lokale Variable" Fehler sagt mir sonst ... –

+0

Sorry - es "sollte nicht" eins sein, wie die Objektinitialisierer erklärt werden. Das erste Objekt wird erstellt, und dann werden seinen Parametern Werte zugewiesen. Da ich dies in dem Stil tun kann, den die Objekt-Initiatoren angeblich kompilieren, sollte ich es mit Objektinitialisierern machen können. –

2

Das können Sie nicht mit Objektinitialisierern tun. Allerdings können Sie den Trick mit propery Code tun:

class A 
{ 
    B b; 

    public B B 
    { 
     set 
     { 
      b = value; 
      b.a = this; 
     } 
     get 
     { 
      return b; 
     } 
    } 
} 

class B 
{ 
    public A a; 
} 

Nannte es:

var a = new A { B = new B() }; 
0

Ihr Beispiel nicht gut Klasse widerspiegelt Design, IMO; es ist unangemessen zusammenhängend und schafft eine zirkuläre Referenz. Das macht es unmöglich, sie in einem einzigen Ausdruck zusammenzufassen.

Ich würde vorschlagen, dass Sie zurück zum Zeichenbrett gehen und Ihre Klassen in eine Eltern/Kind-Beziehung umgestalten. Ich würde die Konstruktorinjektion für die Kindklasse verwenden und das Kind dem Elternteil mitteilen, dass es ein Kind davon ist.

Zum Beispiel:

public class ParentClass 
{ 
    public List<ChildClass> Children; 
    public void AddChild(ChildClass child) 
    { 
     Children.Add(child); 
     // or something else, etc. 
    } 
    // various stuff like making sure Children actually exists before AddChild is called 
} 

public class ChildClass 
{ 
    public ParentClass Parent; 
    public ChildClass(ParentClass parent) 
    { 
     Parent = parent; 
     Parent.AddChild(this); 
    } 
} 

Dann in Ihrem Telefonvorwahl:

var parent = new ChildClass(new ParentClass()).Parent; 

Und, ja, die in LINQ funktioniert:

// qry, etc. 
select new ChildClass(new ParentClass()).Parent 

aber dann, wie mache ich alle ChildClass haben die gleiche ParentClass Instanz? - Andy Hohorst

Dann müssen Sie die Elternklasse im Voraus kennen.

var parent = new ParentClass(); 
var child = new ChildClass(parent); 

oder

var parent = new ParentClass(); 
// qry, etc. 
select new ChildClass(parent) 
+0

aber wie mache ich dann alle ChildClass haben die gleiche ParentClass-Instanz? –