4
\$\begingroup\$

I would like an abstract class, AbstractComponentAggregate, which has many AbstractComponents. Users of my framework will create their components by extending AbstractComponent and appending the component to this AbstractComponentAggregate.

I would like to know if there is an elegant way to have AbstractComponentAggregate to store a dictionary of the concrete classes that extend AbstractComponents. Note, that I want to store many concrete classes, each of which is an AbstractComponent. This is different than storing a list of AbstractComponents.

Thus, you can invoke a method on AbstractComponentAggregate, such as:

AbstractComponentAggregate.GetComponent(), which returns type T. T is the concrete class, and we ensure T is of type AbstractComponent.

Something similar to this is done in Unity3d, and I would like to emulate this.

\$\endgroup\$

2 Answers 2

4
\$\begingroup\$

I would like to know if there is an elegant way to have AbstractComponentAggregate to store a dictionary of the concrete classes that extend AbstractComponents. Note, that I want to store many concrete classes, each of which is an AbstractComponent. This is different than storing a list of AbstractComponents.

Assuming the type of your dictionary key is K, then Dictionary<K, AbstractComponent> will allow you to store instances of concrete AbstractComponent subclasses (such as FooComponent and BarComponent):

var items = new Dictionary<K, AbstractComponent>();
items[K0] = new FooComponent();
items[K1] = new BarComponent();

GetComponent can look something like:

public T GetComponent<T>() {
   // I'm assuming T's type is the dictionary key here.
   AbstractComponent result;
   if(!items.TryGetValue( typeof(T), out result ) ) {
       // Fail here, perhaps with an exception?
   }

   return (T)result;
}

This would raise an InvalidCastException if the AbstractComponent wasn't really an instance of the concrete class T. You could use GetType() on the result variable and compare the types directly if you wanted to raise a more user-friendly exception.

The code sample assumes you're talking about C#, but the concepts in general are transferable.

\$\endgroup\$
2
  • \$\begingroup\$ Thanks, Josh. I guess my brain wasn't functioning this morning. \$\endgroup\$
    – Alex
    Commented May 4, 2012 at 18:59
  • \$\begingroup\$ I should mention that I am using C++, which made this problem non-trivial. However, using boost::any I was able to emulate C#'s functionality of Type. In conjunction with boost::any_cast and boost::any_bad_cast, I was able to solve the problem. I believe using boost::variant might be a better solution as it can emulate Java's wildcard abilities; however I'll look into that in a later date. For now, I might throw in some BOOST_STATIC_ASSERTS about T to ensure it is of type Component. \$\endgroup\$
    – Alex
    Commented May 6, 2012 at 16:42
2
\$\begingroup\$

Here you go. It's a primitive version of the Implementation I used in Duality which is more or less inspired by Unity.

public sealed class GameObject
{
    private Dictionary<Type,Component> compMap = new Dictionary<Type,Component>();


    public T GetComponent<T>() where T : Component
    {
        return GetComponent(typeof(T)) as T;
    }
    public Component GetComponent(Type t)
    {
        Component result;
        if (this.compMap.TryGetValue(t, out result))
            return result;
        else
            return null;
    }

    public T AddComponent<T>() where T : Component, new()
    {
        if (this.compMap.ContainsKey(typeof(T))) return this.compMap[typeof(T)] as T;
        T newComp = new T();
        return this.AddComponent<T>(newComp);
    }
    public Component AddComponent(Type t)
    {
        if (!typeof(Component).IsAssignableFrom(t)) return null;
        if (this.compMap.ContainsKey(t)) return this.compMap[t];
        Component newComp = Activator.CreateInstance(t, true) as Component;
        return this.AddComponent<Component>(newComp);
    }
    public T AddComponent<T>(T newComp) where T : Component
    {
        Type cType = newComp.GetType();
        if (this.compMap.ContainsKey(cType)) return this.compMap[cType] as T;

        this.compMap.Add(cType, newComp);

        return newComp;
    }
    public T RemoveComponent<T>() where T : Component
    {
        return this.RemoveComponent(typeof(T)) as T;
    }
    public Component RemoveComponent(Type t)
    {
        Component cmp = this.GetComponent(t);
        if (cmp != null) this.RemoveComponent(cmp);
        return cmp;
    }
    public void RemoveComponent(Component cmp)
    {
        this.compMap.Remove(cmp.GetType());
    }
    public void ClearComponents()
    {
        this.compMap.Clear();
    }
}

Hope this helps.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .