views:

94

answers:

3

I have the following class and my question lies with it's GetComponent function

class GameObject
{ 

    private List<GameComponent> _components = new List<GameComponent>();
    public void AddComponent(GameComponent component)
    void RemoveComponent(string name)
    public T GetComponent<T>(string name) where T : GameComponent

}

class GameComponent
{
    public string Name { get; set; }
}

So in GetComponent i want to get the component identified by name and return it as T.

So i would call
NavigationComponent navigation = gameObject.GetComponent<NavigationComponent>("navigation");
where NavigationComponent would be a subclass of GameComponent

Since each NavigationComponent instance is only ever going to have the same name, "navigation", i am pondering if i can do something to simplify the GetComponent to the following

NavigationComponent navigation = gameObject.GetComponent<NavigationComponent>();

Thanks in advance.

Update 1

Well i should mention that the GameObject works like a set of GameObjects, identified by their name, so there can be only one of each in it.

So to elaborate lets say we have
class SimpleNavigation : GameComponent {...}
class AdvancedNavigation : GameComponent {...}
class SensorSystem : GameComponent {...}

Then i would give SimpleNavigation and AdvancedNavigation the Name "navigation" and to the SensorSystem the name "sensor"

So i cannot have both SimpleNavigation and AdvancedNavigation in the above example.

Hope that makes sense.

Thanks in advance.

p.s. I fear that i might be placing too many constrains when there isn't much to gain and longterm the specifications might change, but that is outside of the scope of the question i guess

+1  A: 

Rewritten:

You've mentioned that .Name should remain as a property on your GameComponent class--so one option to consider is overriding the equality operator to say "if two instances of GameComponent have the same name then they should be considered equal". Combined with Dave's suggestion of using OfType() you would have something like this:

class GameObject
{ 

  private List<GameComponent> _components = new List<GameComponent>();

  public T GetComponent<T>(string name) where T : GameComponent, new
  {
    return
      (from c in _components.OfType<T>()
      where c == new T { Name = name }
      select c).FirstOrDefault();
  }
} 

class GameComponent
{
  public string Name { get; set; }

  public bool Equals(GameComponent other)
  {
    if (ReferenceEquals(null, other))
    {
      return false;
    }
    if (ReferenceEquals(this, other))
    {
      return true;
    }
    return Equals(other.Name, Name);
  }

  public override bool Equals(object obj)
  {
    if (ReferenceEquals(null, obj))
    {
      return false;
    }
    if (ReferenceEquals(this, obj))
    {
      return true;
    }
    if (obj.GetType() != typeof(GameComponent))
    {
      return false;
    }
    return Equals((GameComponent)obj);
  }

  public override int GetHashCode()
  {
    return (Name != null ? Name.GetHashCode() : 0);
  }
}

It's worth thinking through whether or not you want to implement this approach--it will affect any equality-checks (not reference-checks). However it's a very powerful ability to say "X and Y should be considered equal when X"--and equality overloading lets you implement that logic in a single centralized home (right on the entity).

STW
I have already implemented the methods above, the GetComponent with FirstOrDefault and a lamda, but i thought i wasn't needed to show it since i actually wanted to change it. On the first note, because i want to have GameObjects that might have a NavigationComponent and a SensesComponent etc. On the second note, to avoid casting it from where i call it, less lines of code where i want to call it.
Xtapodi
ahh, I didn't understand the intent of your question. I've updated my answer with another approach to consider--it might not be what you need in *this* case, but it's worth considering
STW
+1  A: 

Firstly, if all NavigationComponents will have the same name, how will you distinguish between them? If you only want to return a single NavigationComponent, you will need a unique identifier.

You can use the OfType() method to return all instances of a type:

var navigationComponents = _components.OfType<NavigationComponent>();

From there, you can select a single NavigationComponent by some unique identifier:

NavigationComponent nc = navigationComponents.Where(n => n.Id == 1);

Dave Swersky
Ah very nice, that seems to be what i am looking for. Well so far i can think of only needing one component of each type, but not all GameObjects have the same set of components, one might have a Navigation and a Sensor, one might have only a Sensor etc.
Xtapodi
Your answer made me think a little bit better about what i wanted to achieve, i have updated the question.
Xtapodi
+1  A: 

Update: In response to your edit:

Then i would give SimpleNavigation and AdvancedNavigation the Name "navigation" and to the SensorSystem the name "sensor"

If this is the case, then you really can't hope to have an interface like this...

public T GetComponent<T>() where T : GameComponent { }

...because multiple GameComponent object types may potentially share the same name. So you cannot infer name from type.

Why not go with an enum instead? Then you might have something like this:

public enum ComponentType
{
    Navigation,
    Sensor // etc.
}

And your GameObject class could in turn hold a Dictionary<ComponentType, GameComponent>.

Then your GetComponent method could look like this:

GameComponent GetComponent(ComponentType type) { }

Anyway, it's a thought...


I think what makes more sense is this:

Firstly, if you're looking up by name, use a Dictionary<string, GameComponent> instead of a List<GameComponent>. This will give you O(1) lookup time instead of O(N).

Now, if you want name to be dependent on type, define your GameComponent class like this:

class GameComponent
{
    public virtual string Name
    {
        // You could also hard-code this or cache it
        // for better performance.
        get { return this.GetType().Name; }
    }
}

You might then choose to override the Name property in your derived methods, like so:

class NavigationComponent : GameComponent
{
    public override string Name
    {
        // This returns the same thing as GetType().Name,
        // but it's faster.
        get { return "NavigationComponent"; }
    }
}

Then your AddComponent method would be:

public void AddComponent(GameComponent component)
{
    _components.Add(component.Name, component);
}

and your GetComponent method would be simple:

public T GetComponent<T>() where T : GameComponent
{
    return _components[typeof(T).Name] as T;
}
Dan Tao
I havent typed in your code yet, but how can i call .Name on the type if it isn't static?
Xtapodi
@Xtapodi: Calling `this.GetType().Name` will return the name of the object's *type* (itself a `System.Type` object). This would normally not make sense, except that you've specified you actually want the name of a `GameComponent` object to be the same as the name of its type. So for this reason, the [`Type.Name`](http://msdn.microsoft.com/en-us/library/system.reflection.memberinfo.name.aspx) property is precisely what you want.
Dan Tao
Thank you for your new update. If something like virtual static methods was possible then i suppose i could do it, i hoped that some solution might exist with attributes, which it might, but i guess it will be rather expensive performance wise to put in there and i wouldn't be able to enforce it anyway. But i have never used attributes so i might be wrong. Could i enforce that GameObject subclasses have a [ComponentType(string name)] attribute?
Xtapodi