0

Lets say I have the following class structure:

public class NewClass
{
    public string Name{get;set;}
    public Int Age{get;set;}
}

public class ChildClass
{
     public List<NewClass> NewClassList {get;set;} 
     //SomeOther properties
}

public class ParentClass
{
     public List<NewClass> NewClassList {get;set;}
     public List<ChildClass> ChildClassList {get;set;}           
}

My aim is to get ChildClass.NewClassList to reference the value in ParentClass.NewClassList

I want to do this because each instance of Child class needs to access the information held in ParentClass.NewClassList, but ParentClass.ChildClassList can be a very large list, so I do not want to hold copies - I want it to reference the value held.

Also, If I do this:

var firstParent = new ParentClass
{
    ChildClassList = new List<ChildClass> {//lots of entries},
    NewClassList  = new List<NewClass>()
}

Then, further down in my code, I want to assign a value to firstParent.NewClassList and I want it to automatically populate all the values in the child list.

I hope this is all clear?

EDIT: I have edited NewClassList to be initialised, but it does not update the child reference, therefor I assume it is not assigned by reference?

Alex
  • 3,730
  • 9
  • 43
  • 94
  • 5
    a `List` is an object, so the underlying field *is a reference*; if you assign the same `List` instance to both properties: that's all you need. That works right now...? – Marc Gravell Jun 05 '19 at 12:43
  • 2
    List is a list of references, not of copies. Use it as is. – Steve Todd Jun 05 '19 at 12:43
  • @MarcGravell can you elaborate, I have not been able to find a way to assign this variable by reference... – Alex Jun 05 '19 at 12:47
  • @SteveTodd can you elaborate? – Alex Jun 05 '19 at 12:47
  • 1
    You could create a factory method of Parent that creates a child, that passes its internal backing variable to the constructor of Child. That way the two can share a reference. – Steve Todd Jun 05 '19 at 12:48
  • `foreach(var child in firstParent.ChildClassList) { child.NewClassList = firstParent.NewClassList; }`? – Lee Jun 05 '19 at 12:49
  • 2
    @Alex a `List` "value" **is** a reference - I very much doubt you actually need to assign "by reference" here - in almost all cases, assigning a reference *by value* does everything you would want. – Marc Gravell Jun 05 '19 at 12:49
  • 1
    I think there is a misunderstanding here. The Properties in both classes reference the same instance of the List. So if you modify the list, it will be reflected through both properties. *BUT* if you assign a different list to one of the properties, the other class' property will *not* automagically reference that same list. – Fildor Jun 05 '19 at 12:50
  • @Fildor I agree with you that this is the case. SO I guess the way around this is to push onto the list, rather than assigning.... – Alex Jun 05 '19 at 12:51
  • 1
    @Alex so... `var child = new ChildClass { NewClassList = firstParent.ChildClassList };` - job done - this uses the **exact same list** (not a clone) in both places; any change made via either `child` or `firstParent` will be visible to "either" (where "either" here is obtuse because there's **only one list**) – Marc Gravell Jun 05 '19 at 12:52
  • @Alex, you have setters on your List objects. You can replace the list with another simply by setting the value. This doesn't copy one list to another, it replaces one pointer with another. – Steve Todd Jun 05 '19 at 12:53
  • @Fildor that is the correct answer, if you want to add it, I will accept it – Alex Jun 05 '19 at 12:56
  • @Alex, honestly, I would not know what to write. Because I think all this was only a misunderstanding, somehow. An answer that I could accept would describe how you can do, what you originally described. – Fildor Jun 05 '19 at 13:03
  • ... and thinking about it, I think another level of indirection can do the job. I.e. that behavior is exactly what a Model would do for different ViewModels it gets injected into. Of course, that would mean you have to be able to "inject" something like a model to both parent and child ... – Fildor Jun 05 '19 at 13:11
  • Does the child class need to be able to *access* the list found in the parent or *modify* it? If the answer is that it should only read the list, there are some different approaches. We should decide up front what modifications are allowed and why, and limit access as much as possible. – Scott Hannen Jun 05 '19 at 13:25
  • @ScottHannen it only need to access the list - what other options would you suggest? – Alex Jun 05 '19 at 13:40
  • I spoke a bit too soon because even if you give one class a read-only reference to the collection, it's still exposed as a list. Other classes can add or remove items. That makes the intent less clear. Whichever class "owns" the list, it's better if that class also controls access to the list. Instead of exposing the list, it hides the list and controls what gets added or deleted. Other classes can see what's in the list but they can't modify the list. – Scott Hannen Jun 05 '19 at 14:17
  • @ScottHannen But couldn't we do all that with a Model-Like approach? We just had to define the interfaces appropriately. I am thinking of ReadOnlyCollection etc. – Fildor Jun 05 '19 at 15:43
  • @Fildor - that's what i was thinking too. But now my understanding is less clear. I can offer general ideas on how and why to encapsulate collections but nothing that's specific to this question. – Scott Hannen Jun 05 '19 at 15:58
  • Because your code in fact already does what you want it to do, arguably your question is more appropriately closed due to lack of a [mcve] that reproduces the problem (since there is no problem). But, the marked duplicates IMHO offer better insight and help for you to understand _why_ the code already does what you want it to. I also recommend the related SO question here: https://stackoverflow.com/questions/436211/is-everything-in-net-an-object. It's not really exactly what you're asking, but it is also something you'll want to read about. – Peter Duniho Jun 05 '19 at 17:40

1 Answers1

3

Both your custom class, NewClass, and List<T> are reference types.

From Microsoft's documentation on Reference Types:

There are two kinds of types in C#: reference types and value types. Variables of reference types store references to their data (objects), while variables of value types directly contain their data. With reference types, two variables can reference the same object; therefore, operations on one variable can affect the object referenced by the other variable. With value types, each variable has its own copy of the data, and it is not possible for operations on one variable to affect the other (except in the case of in, ref and out parameter variables; see in, ref and out parameter modifier).

[emphasis mine]

So, to accomplish what you want all you'd have to do is assign each child's NewClassList property to its parent's NewClassList.

var firstParent = new ParentClass
{
    NewClassList = new List<NewClass>(),
    ChildClassList = new List<ChildClass>()
};
firstParent.ChildClassList.Add(new ChildClass
{
    NewClassList = firstParent.NewClassList
});
firstParent.ChildClassList.Add(new ChildClass
{
    NewClassList = firstParent.NewClassList
});
firstParent.NewClassList.Add(new NewClass
{
    Name = "Hugh Mann",
    Age = 48
});
//firstParent and both children now contain Hugh Mann.

firstParent.ChildClassList[0].NewClassList.Add(new NewClass
{
    Name = "Sillius Soddus",
    Age = 43
});
//firstParent and both children now contain Sillius Soddus.
    
firstParent.ChildClassList[1].NewClassList.Add(new NewClass
{
    Name = "Joanna Dance",
    Age = 62
});
//firstParent and both children now contain Joanna Dance.
    
firstParent.NewClassList[0].Age = 23;
//Hugh Mann now has an age of 23 in firstParent and its children

If you were to assign a different list to either the parent or the child they would no longer be referencing the same list. Changes to one list would not occur on the other as they are referencing completely different lists.

var firstParent = new ParentClass
{
    NewClassList = new List<NewClass>(),
    ChildClassList = new List<ChildClass>()
};
firstParent.ChildClassList.Add(new ChildClass
{
    NewClassList = firstParent.NewClassList
});
    
firstParent.ChildClassList[0].NewClassList.Add(new NewClass
{
    Name = "Sillius Soddus",
    Age = 43
});
//firstParent and its child now contain Sillius Soddus.
    
firstParent.NewClassList = new List<NewClass>
{
    new NewClass
    {
        Name = "Hugh Mann",
        Age = 22
    }
};
//firstParent.NewClassList now references a totally different list. It contains Hugh Mann, while firstParent.ChildClassList[0] contains Sillius Soddus.
    
firstParent.NewClassList.Add(new NewClass
{
    Name = "Ian D. Dark",
    Age = 33
});
//firstParent.NewClassList now contains Hugh Mann and Ian D. Dark. Since firstParent.ChildClassList[0] references a totally different list it still only contains Sillius Soddus.
Community
  • 1
  • 1
Joshua Robinson
  • 3,399
  • 7
  • 22