2009-04-27 10 views
8

así, inicializadores de objeto son todo tipo de práctica - especialmente si usted está haciendo LINQ, en las que son francamente necesaria - pero no puedo averiguar por éste:uso anidado de Inicializadores C# objetos

public class Class1 { 
    public Class2 instance; 
} 

public class Class2 { 
    public Class1 parent; 
} 

utilizando la siguiente manera:

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

como un inicializador:

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

esto no funciona, es supuestamente class1 una variable local no asignada. se pone aún más difícil en LINQ cuando estás haciendo algo así como

select new Class1() { ... 

que ni siquiera tiene un nombre para referirse a ella por!

¿cómo puedo evitar esto? ¿Puedo simplemente no hacer referencias anidadas usando los inicializadores de objetos?

Respuesta

6

puede simplemente no hacer referencias anidadas utilizando inicializadores de objeto?

Tienes razón, no puedes. Habría un ciclo; A requiere B para la inicialización pero B requiere A antes. Para ser precisos, puede hacer, por supuesto, inicializadores de objetos anidados pero no con dependencias circulares.

Pero puede, y le sugiero que, de ser posible, trabaje de la siguiente manera.

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

La construcción de la relación dentro de la propiedad tiene la benifit que no se puede conseguir un estado inconsitent si se olvida de uno de los estados de inicialización. Es bastante obvio que esta es una solución subóptima porque necesita dos declaraciones para hacer una cosa.

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

Con la lógica en las propiedades, se hace lo que se hace con una sola instrucción.

a.Child = b; 

O a la inversa.

b.Parent = a; 

Y finalmente con la sintaxis del inicializador de objetos.

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

me gusta esta respuesta mejor, pero no funciona para mí, porque me he dejado que Clase2 es en realidad Lista , para hacer más clara los ejemplos. drat, fue contraproducente. –

1

Este problema no es específico de los inicializadores de objetos, es una restricción general en el lenguaje C#. No puede usar una variable local hasta que se haya asignado definitivamente. Aquí es una simple repro

Class1 Foo(Class1 c1) { 
    return c1; 
} 

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

No creo que hay alguna forma de evitar esto, cuando la aplicación se instancia del objeto Class1 se necesita haber creado el objeto Clase2 primero para que sepa dónde se encuentra en la memoria de la referencia. Esto se puede ver si se intenta lo siguiente:

 Class1 myClass1 = null; 

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

Esto compilar y ejecutar, pero la propiedad de los padres de la clase 2 será nula ya que eso es lo que el valor estaba en el momento en que se ejecutó la línea interior de código, lo que significa que la línea más interna fue la primera en ejecutarse.

0

Sin duda, puede utilizar los inicializadores de objetos anidados, lo hago todo el tiempo.

Sin embargo, en su caso particular, sus objetos tienen una referencia circular. Debe finalizar la creación de una instancia antes de asignarla a la otra. De lo contrario, le está entregando al compilador un clásico chicken and egg problem que no puede manejar.

+0

no es un problema de huevo y gallina, al menos no cómo entiendo los inicializadores: solo azúcar sintáctica para declarar un objeto y establecer sus propiedades. –

+0

Bueno, el error de "variable local no asignada" me dice lo contrario ... –

+0

lo siento, "no debería" ser uno según cómo se explican los inicializadores de objetos. El primer objeto se crea, y luego sus parámetros son valores asignados. Como puedo hacer esto en el estilo que supuestamente compilan los iniciailizadores de objetos, debería poder hacerlo con los inicializadores de objetos. –

2

No puede hacer eso con los Inicializadores de objetos. Sin embargo, se puede hacer el truco utilizando el código Propery:

class A 
{ 
    B b; 

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

class B 
{ 
    public A a; 
} 

Llamándola:

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

Su ejemplo no refleja el buen diseño de clase, la OMI; es inapropiadamente cohesivo y crea una referencia circular. Eso es lo que hace que sea imposible crear una instancia juntos en una sola expresión.

Le sugiero que vuelva a la pizarra y refactorice sus clases en una relación padre/hijo. Usaría la inyección de constructor en la clase de niño y le pediría al niño que diga que es hijo de ella.

Por ejemplo:

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

Luego, en su Código de llamada:

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

Y, sí, que funciona en LINQ:

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

pero entonces, ¿cómo Cómo hago que todos los ChildClass tengan la misma instancia ParentClass ? - Andy Hohorst

Luego debe conocer la clase de padres por adelantado.

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

o

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

, pero ¿cómo hago que todas las ChildClass tengan la misma instancia ParentClass? –

Cuestiones relacionadas