user502255
user502255

Reputation:

Collections of Derived Child Classes

Is there an accepted method of adding possibly-derived objects to a collection without allowing instances of the base or derived objects to be created on their own? I'm thinking that's almost a contradiction in terms.

About the only method I've been able to come up with is to add to the parent collection from the child's base implementation like this:

// Child constructors
private void Child() { }
protected void Child(Parent parent)
{
    parent.Collection.Add(this);
}

This forces the child object to always be instantiated with a parent, but it seems like a rather messy implementation to add a child class to a parent collection from the child. I know I can pass a Type-type variable to a method, and this might be the way to go, but I'm not sure how to create/cast to the passed type.


Update: I'm playing around with code that looks like this as a possible generic ChildCollection.Add method, in case this gives anybody a better picture of what I want...we'll see if it works in the long run:

// Currently testing directly in Parent class;
// can later be moved/modified for Parent's ChildCollection class.
public Child AddTest(string info, Type derivedType)
{
    ConstructorInfo ci = derivedType.GetConstructor(new Type[] { typeof(Parent) });
    Child myBaby = (Child) ci.Invoke(new Object[] { this });
    myBaby.Initialize(info);
    return myBaby;
}

This could then be called with code like:

Child newChild = Parent.AddTest("Hello World", typeof(DerivedChild));

Upvotes: 2

Views: 1447

Answers (3)

user502255
user502255

Reputation:

Ultimately, I've gone for code very similar to what I posted in my update. I'm posting it here, as I think it's a useful technique for either a generic object factory or a limited one (in this case, limited to those that derive from the Child class).

The basic idea is to create a custom collection, then use an Add method (or perhaps I should name it Create?) to instantiate the object and properly handle any overridden initialization in a Child derivative.

Here's a skeleton of the code I ended up with:

// Use standard Child
public Child Add(string initInfo)
{
    Child newChild = new Child(this.Parent);
    // There's actually a bit more coding before Initialize()
    // in the real thing, but nothing relevant to the example.
    newChild.Initialize(initInfo);
    List.Add(newChild);
    return newChild;
}

// Overload for derived Child.
public Child Add(Type childDerivative, string initInfo)
{
    if (!childDerivative.IsSubclassOf(typeof(Child)))
        throw new ArgumentException("Not a subclass of Child.");
    ConstructorInfo ci = childDerivative.GetConstructor(
        BindingFlags.Instance |
        BindingFlags.Public | BindingFlags.NonPublic |
        BindingFlags.FlattenHierarchy |
        BindingFlags.ExactBinding, 
        null, new Type[] { typeof(Parent) }, null);
    if (ci == null)
        throw new InvalidOperationException("Failed to find proper constructor.");
    newChild = (Child)ci.Invoke(new Object[] { this.Parent });
    newChild.Initialize(initInfo);
    List.Add(newChild);
    return newChild;
}

Since this may not cover every possible derived Child the client application might want to create (particularly if they're adding their own parameters to the constructor), I'm thinking I'll probably also provide an Add(Child child) method, with the caveat that if the user is instantiating the Child object with a standard "new Child(Parent)", they're also responsible for taking all the standard initialization steps in the expected manner.

Upvotes: 1

bricklayer137
bricklayer137

Reputation: 384

The restriction is that a child object shouldn't exist without the parent. How about exposing a property like "IsAttached" or "Parent" and use internal methods to attach child objects to the parent? Then the child objects can be constructed anywhere in your code base but won't be a valid child object until attached.

- internal Attach(parent) 
- internal Detach() 
- public IsAttached

The parent collection can then implement methods to add child objects and perform some validation. I'm thinking along the lines of Forms and Controls.

Collection.Add(child) 
{
    // throw error if the child is not the right type
    child.Attach(this)
    base.add(child)    
} 

Upvotes: 0

jtdubs
jtdubs

Reputation: 13983

If you were serious about not needing to be able to CREATE instances of the parent type, just make its constructor protected or declare it abstract.

Then, you can create subclass instances and add them to a list easily, and it will be impossible to create instances of the base class.

Upvotes: 0

Related Questions