Skip to content

Commit

Permalink
Refactor TypeDictionary.
Browse files Browse the repository at this point in the history
- When removing entries, ensure the dictionary entry is removed is the list is emptied, to prevent incorrect lookups later.
- Prevent NRE when calling GetOrDefault<T> where T is a value type and the default is returned.
- Use a single dictionary for simplicity and improved lookup performance (since there is only one dictionary to check).
- De-duplicate code in Get and GetOrDefault.
  • Loading branch information
RoosterDragon committed Jul 3, 2015
1 parent 5056e7b commit 5c5cbb3
Showing 1 changed file with 26 additions and 43 deletions.
69 changes: 26 additions & 43 deletions OpenRA.Game/Primitives/TypeDictionary.cs
Expand Up @@ -17,8 +17,8 @@ namespace OpenRA.Primitives
{
public class TypeDictionary : IEnumerable
{
Dictionary<Type, object> dataSingular = new Dictionary<Type, object>();
Dictionary<Type, List<object>> dataMultiple = new Dictionary<Type, List<object>>();
static readonly Func<Type, List<object>> CreateList = type => new List<object>();
readonly Dictionary<Type, List<object>> data = new Dictionary<Type, List<object>>();

public void Add(object val)
{
Expand All @@ -32,63 +32,47 @@ public void Add(object val)

void InnerAdd(Type t, object val)
{
List<object> objs;
object obj;

if (dataMultiple.TryGetValue(t, out objs))
objs.Add(val);
else if (dataSingular.TryGetValue(t, out obj))
{
dataSingular.Remove(t);
dataMultiple.Add(t, new List<object> { obj, val });
}
else
dataSingular.Add(t, val);
data.GetOrAdd(t, CreateList).Add(val);
}

public bool Contains<T>()
{
return dataSingular.ContainsKey(typeof(T)) || dataMultiple.ContainsKey(typeof(T));
return data.ContainsKey(typeof(T));
}

public T Get<T>()
{
if (dataMultiple.ContainsKey(typeof(T)))
throw new InvalidOperationException("TypeDictionary contains multiple instance of type `{0}`".F(typeof(T)));

object ret;
if (!dataSingular.TryGetValue(typeof(T), out ret))
throw new InvalidOperationException("TypeDictionary does not contain instance of type `{0}`".F(typeof(T)));
return (T)ret;
return (T)Get(typeof(T), true);
}

public T GetOrDefault<T>()
{
return (T)GetOrDefault(typeof(T));
var result = Get(typeof(T), false);
if (result == null)
return default(T);
return (T)result;
}

public object GetOrDefault(Type t)
object Get(Type t, bool throwsIfMissing)
{
if (dataMultiple.ContainsKey(t))
throw new InvalidOperationException("TypeDictionary contains multiple instances of type `{0}`".F(t));

object ret;
if (!dataSingular.TryGetValue(t, out ret))
List<object> ret;
if (!data.TryGetValue(t, out ret))
{
if (throwsIfMissing)
throw new InvalidOperationException("TypeDictionary does not contain instance of type `{0}`".F(t));
return null;
return ret;
}
if (ret.Count > 1)
throw new InvalidOperationException("TypeDictionary contains multiple instances of type `{0}`".F(t));
return ret[0];
}

public IEnumerable<T> WithInterface<T>()
{
List<object> objs;
object obj;

if (dataMultiple.TryGetValue(typeof(T), out objs))
if (data.TryGetValue(typeof(T), out objs))
return objs.Cast<T>();
else if (dataSingular.TryGetValue(typeof(T), out obj))
return new T[] { (T)obj };
else
return new T[0];
return new T[0];
}

public void Remove<T>(T val)
Expand All @@ -104,12 +88,11 @@ public void Remove<T>(T val)
void InnerRemove(Type t, object val)
{
List<object> objs;
object obj;

if (dataMultiple.TryGetValue(t, out objs))
objs.Remove(val);
else if (dataSingular.TryGetValue(t, out obj))
dataSingular.Remove(t);
if (!data.TryGetValue(t, out objs))
return;
objs.Remove(val);
if (objs.Count == 0)
data.Remove(t);
}

public IEnumerator GetEnumerator()
Expand Down

0 comments on commit 5c5cbb3

Please sign in to comment.