Complexity of Dictionary - C#

Started by
15 comments, last by jameszhao00 12 years, 5 months ago

All of the above would work better if it were turned the other way round:
class HudElement {
...
public void RenderToSpriteBatch(SpriteBatch s) {
s.draw(elemText, position, spr.sourceRect, elemColor, 0f, Vector2.Zero, spr.Scale,SpriteEffects.None, 0f);
}
...
};


Then:
...
public override void Draw(GameTime gameTime)
{
batch.Begin();
foreach(KeyValuePair<HUDElement.ELEM_TYPE, HUDElement> e in hudElements) {
e.Value.RenderToSpriteBatch(batch);
}
batch.End();
...


Out of curiosity, is the way you structured your code because of how XNA is structured? Or would you take that approach regardless of the game-dev library you used?

Beginner in Game Development?  Read here. And read here.

 

Advertisement

Out of curiosity, is the way you structured your code because of how XNA is structured? Or would you take that approach regardless of the game-dev library you used?


It balances the encapsulation and coupling.

- HudElement doesn't need to expose its internals. It could, for other reasons, but in this case it isn't needed. So HudElement knows how to render itself.
- The container that holds these isn't all that relevant either. If one were to choose array, ArrayList, ..., anything iterable, code remains same (almost).
- Elements to be rendered don't need to be explicitly named. Just render entire container.

Later, one could get fancy. If Hud had to be rendered to something else, one could use delegates or somesuch. If there were multiple containers one could pass the iterator, perhaps custom one.

But code above groups orthogonal concepts.

Advanced topics:
- It's aligned with Inversion of Control, which tends to minimize coupling.
- HudElement is now isolated. Aside from being directly reusable on any SpriteBatch, it can be also tested in isolation.
- foreach contains more contextual information and hints that container is not "clever". If we need something more (specific ordering, subset of elements, ...), it needs to be stated specifically.

[quote name='Alpha_ProgDes' timestamp='1318700912' post='4872917']
Out of curiosity, is the way you structured your code because of how XNA is structured? Or would you take that approach regardless of the game-dev library you used?


It balances the encapsulation and coupling.

- HudElement doesn't need to expose its internals. It could, for other reasons, but in this case it isn't needed. So HudElement knows how to render itself.
- The container that holds these isn't all that relevant either. If one were to choose array, ArrayList, ..., anything iterable, code remains same (almost).
- Elements to be rendered don't need to be explicitly named. Just render entire container.

Later, one could get fancy. If Hud had to be rendered to something else, one could use delegates or somesuch. If there were multiple containers one could pass the iterator, perhaps custom one.

But code above groups orthogonal concepts.

Advanced topics:
- It's aligned with Inversion of Control, which tends to minimize coupling.
- HudElement is now isolated. Aside from being directly reusable on any SpriteBatch, it can be also tested in isolation.
- foreach contains more contextual information and hints that container is not "clever". If we need something more (specific ordering, subset of elements, ...), it needs to be stated specifically.
[/quote]

Ah, I can't really use your way, I need to be able to have a separate draw method for the different elements so I can calculate the sourcewidth of the Healthbar/Armour etc. Now I think of it, that was the reason for the HUDElements not drawing themselve in the first place, they all need to be drawn differently

Speaking to your question, I'd be surprised --extremely surprised-- if: aDictionary.containsKey("blah") was faster or improved in any way than aDictionary["blah"]. If anything, aDictionary.containsKey("blah") would have to do a scan of all keys until it reached the key it was looking for. aDictionary["blah"], IIRC, is the better choice of the two. And definitely should be still O©.

The issue with accessing the Dictionary with the key directly as in: Dictionary[key] is that if the key is not found, it will throw a KeyNotFoundException. The safest method is to actually use a TryGetValue(), which internally does a ContainsKey() lookup and then indexes the dictionary to return the item. It's faster than doing ContainsKey/lookup yourself since you only need a single key lookup call, not two.

In summary,
  • Dictionary[key] : fastest, key not found throws exception
  • Dictionary.TryGetValue() : slightly slower. key not found returns null
  • Dictionary.ContainsKey/Dictionary[key] : equivalent to Dictionary.TryGetValue(), but slower.
[size="2"]Currently working on an open world survival RPG - For info check out my Development blog:[size="2"] ByteWrangler

public enum Position
{
TOP_LEFT, TOP_RIGHT, TOP_MIDDLE, MIDDLE_LEFT, MIDDLE_RIGHT, MIDDLE, BOTTOM_LEFT, BOTTOM_RIGHT, BOTTOM_MIDDLE
}

public void setPosition(Position pos)
{
switch (pos)
{
case Position.TOP_LEFT:
m_position = new Vector2(offsetX, offsetY);
break;
case Position.TOP_MIDDLE:
m_position = new Vector2((Window.ClientBounds.Width / 2 - texture.Width / 2) + offsetX, offsetY);
break;
case Position.TOP_RIGHT:
m_position = new Vector2((Window.ClientBounds.Width - texture.Width) + offsetX, offsetY);
break;

case Position.MIDDLE_LEFT:
m_position = new Vector2(offsetX, (Window.ClientBounds.Height / 2 - texture.Height / 2) + offsetY);
break;
case Position.MIDDLE:
m_position = new Vector2((Window.ClientBounds.Width / 2 - texture.Width / 2) + offsetX, (Window.ClientBounds.Height / 2 - texture.Height / 2) + offsetY);
break;
case Position.MIDDLE_RIGHT:
m_position = new Vector2((Window.ClientBounds.Width - texture.Width) + offsetX, (Window.ClientBounds.Height / 2 - texture.Height / 2) + offsetY);
break;

case Position.BOTTOM_LEFT:
m_position = new Vector2(offsetX, (Window.ClientBounds.Height - texture.Height) + offsetY);
break;
case Position.BOTTOM_MIDDLE:
m_position = new Vector2((Window.ClientBounds.Width / 2 - texture.Width / 2) + offsetX, (Window.ClientBounds.Height - texture.Height) + offsetY);
break;
case Position.BOTTOM_RIGHT:
m_position = new Vector2((Window.ClientBounds.Width - texture.Width) + offsetX, (Window.ClientBounds.Height - texture.Height) + offsetY);
break;
}
}


Ew. Why not something like:


private static int pixels<TEnum>(this TEnum alignment, int size) {
return ((int)alignment * size) / 2;
}

public enum VerticalAlignment { TOP = 0, MIDDLE, BOTTOM };
public enum HorizontalAlignment { LEFT = 0, MIDDLE, RIGHT };
public void setPosition(VerticalAlignment v, HorizontalAlignment h) {
m_position = new Vector2(
offsetX + h.pixels(Window.ClientBounds.Width - texture.Width),
offsetY + v.pixels(Window.ClientBounds.Height - texture.Height)
);
}


(Or you could make actual classes with some pre-defined instances instead of banging enums into submission)


public enum Position
{
TOP_LEFT, TOP_RIGHT, TOP_MIDDLE, MIDDLE_LEFT, MIDDLE_RIGHT, MIDDLE, BOTTOM_LEFT, BOTTOM_RIGHT, BOTTOM_MIDDLE
}

public void setPosition(Position pos)
{
switch (pos)
{
case Position.TOP_LEFT:
m_position = new Vector2(offsetX, offsetY);
break;
case Position.TOP_MIDDLE:
m_position = new Vector2((Window.ClientBounds.Width / 2 - texture.Width / 2) + offsetX, offsetY);
break;
case Position.TOP_RIGHT:
m_position = new Vector2((Window.ClientBounds.Width - texture.Width) + offsetX, offsetY);
break;

case Position.MIDDLE_LEFT:
m_position = new Vector2(offsetX, (Window.ClientBounds.Height / 2 - texture.Height / 2) + offsetY);
break;
case Position.MIDDLE:
m_position = new Vector2((Window.ClientBounds.Width / 2 - texture.Width / 2) + offsetX, (Window.ClientBounds.Height / 2 - texture.Height / 2) + offsetY);
break;
case Position.MIDDLE_RIGHT:
m_position = new Vector2((Window.ClientBounds.Width - texture.Width) + offsetX, (Window.ClientBounds.Height / 2 - texture.Height / 2) + offsetY);
break;

case Position.BOTTOM_LEFT:
m_position = new Vector2(offsetX, (Window.ClientBounds.Height - texture.Height) + offsetY);
break;
case Position.BOTTOM_MIDDLE:
m_position = new Vector2((Window.ClientBounds.Width / 2 - texture.Width / 2) + offsetX, (Window.ClientBounds.Height - texture.Height) + offsetY);
break;
case Position.BOTTOM_RIGHT:
m_position = new Vector2((Window.ClientBounds.Width - texture.Width) + offsetX, (Window.ClientBounds.Height - texture.Height) + offsetY);
break;
}
}


Ew. Why not something like:


private static int pixels<TEnum>(this TEnum alignment, int size) {
return ((int)alignment * size) / 2;
}

public enum VerticalAlignment { TOP = 0, MIDDLE, BOTTOM };
public enum HorizontalAlignment { LEFT = 0, MIDDLE, RIGHT };
public void setPosition(VerticalAlignment v, HorizontalAlignment h) {
m_position = new Vector2(
offsetX + h.pixels(Window.ClientBounds.Width - texture.Width),
offsetY + v.pixels(Window.ClientBounds.Height - texture.Height)
);
}


(Or you could make actual classes with some pre-defined instances instead of banging enums into submission)


Hi, sorry for the late reply, but I have never seen this before, do you have any links to explanations to what this is/how it works?

Ew. Why not something like:


private static int pixels<TEnum>(this TEnum alignment, int size) {
return ((int)alignment * size) / 2;
}

public enum VerticalAlignment { TOP = 0, MIDDLE, BOTTOM };
public enum HorizontalAlignment { LEFT = 0, MIDDLE, RIGHT };
public void setPosition(VerticalAlignment v, HorizontalAlignment h) {
m_position = new Vector2(
offsetX + h.pixels(Window.ClientBounds.Width - texture.Width),
offsetY + v.pixels(Window.ClientBounds.Height - texture.Height)
);
}



I didn't know you can generify extension methods...

That is quite clever. He treates enums as a int. He then declared a extension method on a generic, and uses it on a int in this case. Awesome!

This is some mind bending language hackery.

This topic is closed to new replies.

Advertisement