3

In C#, if I have an object that has a collection, is it possible to retrieve the object that contains the collection?

Here is an example:

public class TestObject
{
    public string name { get; set; }
    public TestObjectCollection testObjects{ get; set; } 
}

The TestObjectCollection collection inherits from CollectionBase and is a collection of TestObjects.

Here is an example implementation:

  • A TestObject is created with a name of "Test1"
  • The TestObject with the name of "Test1" has a TestObjectCollection with a TestObject with a name of "Test2"

If I have the TestObject with the name of "Test2", how can I get the TestObject with the name of "Test1"

Thanks

Simon
  • 7,991
  • 21
  • 83
  • 163

4 Answers4

2

The only way to do this is to keep a reference to the parent in the child object. You can do this while creating the child object:

this.testObjects = new TestObjectCollection(this);

Then in TestObjectCollection's constructor:

public TestObject ParentObject { get; set; }

public TestObjectCollection(TestObject parent)
{
    ParentObject = parent;
    ...
}
Yogesh
  • 14,498
  • 6
  • 44
  • 69
  • Many Well Defined Multi-Level Objects (Like DataSets DataTables) formally do this for you in a manner just like the above solution. Key point being that it is formally defined and not a hack where you may destroy a parents object chain cleanup routine in some odd way by keeping a reference that was never expected. DataRow allows access to its DataTable for example. – Sql Surfer Aug 13 '15 at 02:45
  • Just curious: What is the difference between "formally defined" and "a hack"? – Yogesh Aug 13 '15 at 02:48
  • If you have to go through extraordinary lengths in .Net it is a hack. If you are referencing a property that was already there (like above solution) then it is not a hack. If you have to use reflection in 4.0 or 4.5 to do something, it is a hack. – Sql Surfer Aug 13 '15 at 02:58
  • Is there a hack where you can find the parent of an object without storing parent's reference? – Yogesh Aug 13 '15 at 03:01
0

Maybe you can do it like this :

using System;
using System.Collections.Generic;

public class TestObject
{
    private TestObjectCollection _testObjects;

    public string name { get; set; }
    public TestObjectCollection parentCollection { get; set; }
    public TestObjectCollection testObjects 
    { 
        get
        {
            return _testObjects;
        }
        set 
        {
            _testObjects = value;
            _testObjects.parent = this;
        }
    }
}

public class TestObjectCollection
{
    private List<TestObject> _testObjects;

    public TestObject parent { get; set; }

    public TestObjectCollection()
    {
        _testObjects = new List<TestObject>();
    }

    public void Add(TestObject testObject)
    {
        testObject.parentCollection = this;
        _testObjects.Add(testObject);
    }

    public TestObject this[int i] {
        get {
            return _testObjects[i];
        }
    }
}


public class Test
{
    public static void Main()
    {
        // your code goes here
        TestObject test1 = new TestObject();

        TestObject test2 = new TestObject();

        var collection = new TestObjectCollection();
        collection.Add(test2);
        test1.testObjects = collection;

        if (test2.parentCollection.parent == test1)
        {
            Console.WriteLine("Done");
        }
        else
        {
            Console.WriteLine("Fail");
        }
    }
}

I used List as an example.

mad MIckO
  • 26
  • 3
0

Unless you explicitly code that parent-child relationship (as in Yogesh's answer), there's no way to find "the" parent - in large part because there can be more than one such parent:

public class TestObject
{
    public string name { get; set; }
    public TestObjectCollection testObjects{ get; set; } 
}
public class TestObjectCollection : CollectionBase
{
    public void Add(TestObject to)
    {
        this.List.Add(to);
    }
}
void Main()
{
    TestObjectCollection children = new TestObjectCollection();
    TestObject child = new TestObject { name = "child" };
    children.Add(child);

    TestObject parent = new TestObject { name = "parent", testObjects = children }; 
    TestObject otherParent = new TestObject { name = "otherParent", testObjects = children };   
    TestObject stepParent = new TestObject { name = "stepParent", testObjects = children }; 
    TestObject inLocoParentis = new TestObject { name = "inLocoParentis", testObjects = children };
    // and we can keep going on and on and on ...   
}
Edmund Schweppe
  • 4,992
  • 1
  • 20
  • 26
0

If you don't want to pass referrences in the constructors, you can use a static dictionary to keep track of TestObject instances and have the TestObjectCollection look up it's parent from that static dictionary in a lazy loading fashion.

For example

public class TestObject
{
    /// <summary>
    /// Keep a list of all the instances of TestObject's that are created.
    /// </summary>
    internal static Dictionary<Guid, TestObject> _collections = new Dictionary<Guid, TestObject>();

    /// <summary>
    /// An ID to uniquely identify an instance of a TestObject
    /// </summary>
    public Guid ID { get; private set; }

    /// <summary>
    /// A reference to the collection which will be set in the constructor
    /// </summary>
    public TestObjectCollection TestObjects { get; private set; }

    public TestObject()
    {
        //generate the unique id
        this.ID = Guid.NewGuid();
        this.TestObjects = new TestObjectCollection();
        //add this testobject to the List of test objects.
        _collections.Add(this.ID, this);
    }

    /// <summary>
    /// Destructor, kill the TestObject from the list of TestObject's.
    /// </summary>
    ~TestObject()
    {
        if (_collections.ContainsKey(this.ID))
        {
            _collections.Remove(this.ID);
        }
    }
}

public class TestObjectCollection : IEnumerable<TestObject>
{
    private List<TestObject> _testObjects = new List<TestObject>();

    public Guid ID { get; private set; }

    public TestObject this[int i]
    {
        get
        {
            return _testObjects[i];
        }
    }

    private TestObject _Parent = null;
    public TestObject Parent
    {
        get
        {
            if (_Parent == null)
            {
                _Parent = TestObject._collections.Values.Where(p => p.TestObjects.ID == this.ID).FirstOrDefault();
            }
            return _Parent;
        }
    }

    public TestObjectCollection()
    {
        this.ID = Guid.NewGuid();
    }

    public void Add(TestObject newObject)
    {
        if (newObject != null)
            _testObjects.Add(newObject);
    }

    public IEnumerator<TestObject> GetEnumerator()
    {
        return _testObjects.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return _testObjects.GetEnumerator();
    }
}

Testing...

class Program
{
    static void Main(string[] args)
    {
        TestObject tObject = new TestObject();
        Console.WriteLine("TestObject ID: " + tObject.ID);
        Console.WriteLine("TestObject TestObjectCollection ID: " + tObject.TestObjects.ID);
        Console.WriteLine("TestObject TestObjectCollection Parent ID: " + tObject.TestObjects.Parent.ID);
        Console.WriteLine("Press any key...");

        Console.ReadKey(true);
    }
}

So what this does is in the constructor of TestObject it give's itself a GUID ID. Then it creates an Instace of TestObjectCollection.

In the Constructor of TestObjectCollection it give's itself a GUID ID.

Back in the constructor of TestObject it Set's TestObjects to the collection it just created, then adds a referrence to itself to the Dictionary of TestObjects which is static. It uses TestObject's ID as the Key for said Dictionary.

Then in TestObjectCollection it get's the parent collection by looking it up in that Static Dictionary using a property that doesn't set itself till it's called (as you can't determine it in the constructor because the TestObject constructor hasn't added the referrence yet).

    private TestObject _Parent = null;
    public TestObject Parent
    {
        get
        {
            if (_Parent == null)
            {
                _Parent = TestObject._collections.Values.Where(p => p.TestObjects.ID == this.ID).FirstOrDefault();
            }
            return _Parent;
        }
    }
Ryan Mann
  • 5,178
  • 32
  • 42
  • This model could be updated to support serialization by allowing set on TestObject.TestObjects with a lazy load pattern like the Parent property on TestObjectCollection.Parent and then you would remove the TestObjectCollection instantiation from TestObject's Constructor. – Ryan Mann Aug 13 '15 at 03:35
  • Also, even adding child TestObjects to a TestObject.TestObjects collection will still work. So _Collections will basically be a flat list of All TestObjects outside of their child&parent heirarchy. – Ryan Mann Aug 13 '15 at 03:38