How to ensure that a List in C# always has a specific element?

294 views Asked by At

I have a class named GameObject that has a dynamic List of Components. Those components can be removed and added at any time. A Component could possibly be a Texture, Sound or Transition etc. What's the best way to ensure that this list always has one Transition component? Only one component of each type is allowed.

I have two posibble solutions in my mind. Which one of these is the best? And are there any better solutions to this problem?

Approach 1:

class GameObject {
    private List<Component> components;

    public T GetComponent<T>() where T : Component {
        // search the requested component and return it
        foreach(Component comp in components) {
            if(comp is T) return (T)comp;
        }
        // return null or throw exception when not found
        return null;
    }

    public void RemoveComponent<T>() where T : Component {
        if(typeof(T) != typeof(Transition)) {
            // only remove the componenent if it's not a Transition component
            Component tmp;
            foreach(Component comp in components) {
                if(comp is T) tmp = comp;
                break;
            }
            components.Remove(tmp);
        }
    }
}

Approach 2:

class GameObject {
    // separate variable for the transition component
    private Transition transition;
    private List<Component> components;

    public T GetComponent<T>() where T : Component {
        // if a transition is requestet just return it
        if(typeof(T) == typeof(Transition)) {
            return transition;
        }
        // else: search the requested component in the list
        foreach(Component comp in components) {
            if(comp is T) return (T)comp;
        }
        // return null or throw exception when not found
        return null;
    }

    public void RemoveComponent<T>() where T : Component {
        if(typeof(T) != typeof(Transition)) {
            // only remove the componenent if it's not a Transition component
            Component tmp;
            foreach(Component comp in components) {
                if(comp is T) tmp = comp;
                break;
            }
            components.Remove(tmp);
        }
    }
}
1

There are 1 answers

1
Jaanus Varus On BEST ANSWER

Seems like you are trying to create what is known as an entity component model. I suggest analyzing some engines which use that model, such as the Paradox Game Engine.

The Entity (GameObject) and PropertyContainer (component collection) should be of particular interest for you.

It's probably a good idea to keep the transition component both as a separate field and also store it in the components list. Since it's something that is guaranteed to be part of each game object, you could provide a separate getter property for direct access to this component to bypass any lookup cost.

Since you seem to allow only one component per type, it would be efficient to store the components in a Dictionary<Type, Component>. That would provide really quick lookups in comparison to iterating over the the components and doing type comparisons.

A slightly modified version of your 2nd approach:

class GameObject
{        
    private readonly Transition transition = new Transition();
    private readonly Dictionary<Type, Component> components = new Dictionary<Type, Component>();        

    public GameObject()
    {
        AddComponent(transition);
    }

    public Transition Transition { get { return transition; } }

    public T GetComponent<T>() where T : Component
    {
        Component component;
        if (components.TryGetValue(typeof (T), out component))
        {
            return (T) component;
        }
        return null;
    }

    public void AddComponent<T>(T component) where T : Component
    {
        Type type = typeof (T);
        if (!components.ContainsKey(type))
        {
            components.Add(type, component);
        }
    }

    public void RemoveComponent<T>() where T : Component
    {                        
        if (typeof(T) != typeof(Transition))
        {
            components.Remove(typeof (T));                
        }
    }
}