2009-04-27 13 views
8

donc, initialiseurs d'objets sont toutes sortes de pratique - surtout si vous faites LINQ, où ils sont carrément nécessaires - mais je ne peux pas comprendre tout à fait celui-ci:utilisation emboîtés de C# objet Initialiseur

public class Class1 { 
    public Class2 instance; 
} 

public class Class2 { 
    public Class1 parent; 
} 

en utilisant comme ceci:

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

comme initialiseur:

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

cela ne fonctionne pas, est censé être Class1 une variable locale non affectée. il devient encore plus délicat dans LINQ quand vous faites quelque chose comme

select new Class1() { ... 

il n'a même pas un nom pour désigner par!

Comment puis-je contourner ce problème? puis-je simplement ne pas faire de références imbriquées en utilisant des initialiseurs d'objet?

Répondre

6

je peux tout simplement pas faire des références imbriquées en utilisant initialiseurs d'objets?

Vous avez raison - vous ne pouvez pas. Il y aurait un cycle; A nécessite B pour l'initialisation mais B requiert A avant. Pour être précis - vous pouvez bien sûr faire des initialiseurs d'objets imbriqués mais pas avec des dépendances circulaires.

Mais vous pouvez - et je vous suggère de le faire si possible - travailler comme suit.

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

Construire la relation à l'intérieur de la propriété a le benifit que vous ne pouvez pas obtenir un état de inconsitent si vous oubliez une des instructions d'initialisation. Il est tout à fait évident que c'est une solution sous-optimale parce que vous avez besoin de deux instructions pour faire une chose.

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

Avec la logique dans les propriétés, vous obtenez le résultat avec une seule instruction.

a.Child = b; 

Ou l'inverse.

b.Parent = a; 

Et enfin avec la syntaxe d'initialisation d'objet.

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

J'aime cette réponse mieux, mais cela ne fonctionne pas pour moi, parce que je suis parti que Classe2 est en fait la liste , pour rendre les exemples plus clairs. drat, ça a échoué. –

1

Ce problème n'est pas spécifique aux initialiseurs d'objet, il s'agit d'une restriction générale dans le langage C#. Vous ne pouvez pas utiliser une variable locale tant qu'elle n'a pas été affectée définitivement. Voici un simple repro

Class1 Foo(Class1 c1) { 
    return c1; 
} 

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

Je ne pense pas qu'il y ait de toute façon de contourner ce problème, lorsque l'application est instancié l'objet Class1 il a besoin d'avoir créé l'objet Class2 d'abord pour qu'il sache où en mémoire la référence est situé. Vous pouvez voir si vous procédez comme suit:

 Class1 myClass1 = null; 

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

Cela compiler et exécuter, mais la propriété parent de Classe2 sera nulle puisque c'est ce que la valeur était au moment où la ligne intérieure du code a été exécuté, ce qui signifie que la ligne la plus interne était la première à être exécutée.

0

Vous pouvez certainement utiliser des initialiseurs d'objet imbriqués, je le fais tout le temps.

Cependant, dans votre cas particulier, vos objets ont une référence circulaire. Vous devez terminer l'instanciation avant de l'affecter à l'autre. Sinon, vous donnez au compilateur un chicken and egg problem classique qu'il ne peut pas gérer.

+0

ce n'est pas un problème de poule et d'oeuf, du moins pas comment je comprends les initialiseurs: juste du sucre syntaxique pour déclarer un objet et définir ses propriétés. –

+0

Eh bien, votre erreur "variable locale non assignée" me dit autrement ... –

+0

désolé - il ne devrait pas être un selon la façon dont les initialiseurs d'objet sont expliqués. Le premier objet est créé, puis ses paramètres sont affectés de valeurs. Puisque je peux le faire dans le style que les initiateurs de l'objet devraient compiler, je devrais être capable de le faire avec des initialiseurs d'objets. –

2

Vous ne pouvez pas faire cela avec les initialiseurs d'objet. Cependant, vous pouvez faire l'affaire en utilisant le code propery:

class A 
{ 
    B b; 

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

class B 
{ 
    public A a; 
} 

Qualifiant:

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

Votre exemple ne reflète pas une bonne conception de classe, l'OMI; il est inapproprié et crée une référence circulaire. C'est ce qui rend impossible de les instancier ensemble dans une seule expression.

Je vous suggère de revenir à la planche à dessin et de refactoriser vos classes dans une relation parent/enfant. J'utiliserais l'injection de constructeur sur la classe enfant et demanderais à l'enfant de dire au parent que c'est un enfant.

Par exemple:

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

Ensuite, dans votre code d'appel:

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

Et, oui, cela fonctionne dans LINQ:

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

mais alors comment puis-je faire tous les ChildClass la même instance ParentClass ? - Andy Hohorst

Ensuite, vous devez connaître la classe mère à l'avance.

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

ou

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

mais comment puis-je faire que tous les ChildClass aient la même instance ParentClass? –