Reputation: 409
so, object initializers are all kinds of handy - especially if you're doing linq, where they're downright necessary - but I can't quite figure out this one:
public class Class1 {
public Class2 instance;
}
public class Class2 {
public Class1 parent;
}
using like this:
Class1 class1 = new Class1();
class1.instance = new Class2();
class1.parent = class1;
as an initializer:
Class1 class1 = new Class1() {
instance = new Class2() {
parent = class1
}
};
this doesn't work, class1 is supposedly an unassigned local variable. it gets even trickier in Linq when you are doing something like
select new Class1() { ...
it doesn't even have a name to refer to it by!
how do I get around this? can I simply not make nested references using object initializers?
Upvotes: 9
Views: 3594
Reputation: 56391
Your example does not reflect good class design, IMO; it's inappropriately cohesive and creates a circular reference. That's what makes it impossible to instantiate them together in a single expression.
I would suggest you go back to the drawing board and refactor your classes into a parent/child relationship. I'd use constructor injection on the child class and have the child tell the parent that it's a child of it.
For example:
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);
}
}
Then, in your calling code:
var parent = new ChildClass(new ParentClass()).Parent;
And, yes, that works in LINQ:
// qry, etc.
select new ChildClass(new ParentClass()).Parent
but then how do I make all the ChildClass have the same ParentClass instance? – Andy Hohorst
Then you must know the parent class in advance.
var parent = new ParentClass();
var child = new ChildClass(parent);
or
var parent = new ParentClass();
// qry, etc.
select new ChildClass(parent)
Upvotes: 0
Reputation: 59645
can I simply not make nested references using object initializers?
You are right - you cannot. There would be a cycle; A requires B for initialization but B requires A before. To be precise - you can of course make nested object initializers but not with circular dependencies.
But you can - and I would suggest you should if possible - work this around as follows.
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;
}
Building the relation inside the property has the benifit that you cannot get an inconsitent state if you forget one of the initialization statements. It is quite obvious that this is an suboptimal solution because you need two statements to get one thing done.
b.Parent = a;
a.Child = b;
With the logic in the properties you get the thing done with only one statement.
a.Child = b;
Or the other way round.
b.Parent = a;
And finally with object initializer syntax.
A a = new A { Child = new B() };
Upvotes: 7
Reputation: 5853
You can't do that with Object Initializers. However, you can do the trick using propery code:
class A
{
B b;
public B B
{
set
{
b = value;
b.a = this;
}
get
{
return b;
}
}
}
class B
{
public A a;
}
Calling it:
var a = new A { B = new B() };
Upvotes: 2
Reputation: 11734
You can certainly use nested object initializers, I do it all the time.
However, in your particular case, your objects have a circular reference. You need to finish instantiating one before assigning it to the other. Otherwise, you are handing the compiler a classic chicken and egg problem it can't handle.
Upvotes: 0
Reputation: 28617
I don't think there is anyway to get around this, when the application is instantiating the Class1 object it needs to have created the Class2 object first so that it knows where in memory the reference is located. You can see this if you try the following:
Class1 myClass1 = null;
myClass1 = new Class1()
{
instance = new Class2
{
parent = myClass1
}
};
This will compile and run, but the parent property of Class2 will be null since that is what the value was at the time the inner line of code was run, meaning that the inner most line was the first to be executed.
Upvotes: 2
Reputation: 754565
This problem is not specific to object initializers, it's a general restriction in the C# language. You cannot use a local variable until it has been definitely assigned. Here is a simpler repro
Class1 Foo(Class1 c1) {
return c1;
}
void Example() {
Class1 c1 = Foo(c1);
}
Upvotes: 1