So I’m looking for a dictionary that provides more features than the one that .NET provides. Specifically, I’m looking for extra stuff like insertion and removal of keys/values at a specified index. Also, it would be nice if it played nice with Unity’s serialization system. Any ideas?
[EDIT] Use this instead: http://forum.unity3d.com/threads/finally-a-serializable-dictionary-for-unity-extracted-from-system-collections-generic.335797/
You could use this implementation which uses two lists as keys and values - should provide the functionality you’re asking. If you want it to play well with Unity’s serialization system you have to subclass it, so:
[System.Serializable]
public class StrObjDict : KVPList<string, UnityEngine.Object> { }
and then use it normally, like:
public class Test : MonoBehaviour
{
public StrObjDict dict; // if not public, then annotate with SerializeField
}
Code:
[Serializable]
public class KVPList<TKey, TValue> : IDictionary<TKey, TValue>
{
[SerializeField]
protected List<TKey> keys;
[SerializeField]
protected List<TValue> values;
public List<TKey> Keys { get { return keys; } }
public List<TValue> Values { get { return values; } }
public int Count { get { return keys.Count; } }
public KVPList() : this(new List<TKey>(), new List<TValue>())
{
}
public KVPList(List<TKey> keys, List<TValue> values)
{
this.keys = keys;
this.values = values;
}
public TValue this[TKey key]
{
get
{
int index;
if (!TryGetIndex(key, out index))
{
throw new KeyNotFoundException(key.ToString());
}
return values[index];
}
set
{
int index;
if (!TryGetIndex(key, out index))
{
Add(key, value);
}
else values[index] = value;
}
}
public void SetKeyAt(int i, TKey value)
{
AssertIndexInBounds(i);
if (value != null && !value.Equals(keys*))*
-
AssertUniqueKey(value);*
_ keys = value;_
* }*
* public TKey GetKeyAt(int i)*
* {*
* AssertIndexInBounds(i);*
_ return keys*;
}
public void SetValueAt(int i, TValue value)
{
AssertIndexInBounds(i);
values = value;
}
public TValue GetValueAt(int i)
{
AssertIndexInBounds(i);
return values;
}
public KeyValuePair<TKey, TValue> GetPairAt(int i)
{
AssertIndexInBounds(i);
return new KeyValuePair<TKey, TValue>(keys, values);
}
private void AssertIndexInBounds(int i)
{
if (!keys.InBounds(i))
throw new IndexOutOfRangeException(“i”);
}
public void Clear()
{
keys.Clear();
values.Clear();
}
public void Insert(int i, TKey key, TValue value)
{
AssertUniqueKey(key);
Assert.ArgumentNotNull(key, “Dictionary key cannot be null”);
keys.Insert(i, key);
values.Insert(i, value);
}
private void AssertUniqueKey(TKey key)
{
if (ContainsKey(key))
throw new ArgumentException(string.Format(“There’s already a key {0}
defined in the dictionary”, key.ToString()));
}
public void Insert(TKey key, TValue value)
{
Insert(0, key, value);
}
public void Add(TKey key, TValue value)
{
Insert(Count, key, value);
}
public bool Remove(TKey key)
{
int index;
if (TryGetIndex(key, out index))
{
keys.RemoveAt(index);
values.RemoveAt(index);
return true;
}
return false;
}
public void RemoveAt(int i)
{
AssertIndexInBounds(i);
keys.RemoveAt(i);
values.RemoveAt(i);
}
public void RemoveLast()
{
RemoveAt(Count - 1);
}
public void RemoveFirst()
{
RemoveAt(0);
}
public bool TryGetValue(TKey key, out TValue result)
{
int index;
if (!TryGetIndex(key, out index))
{
result = default(TValue);
return false;
}
result = values[index];
return true;
}
public bool ContainsValue(TValue value)
{
return values.Contains(value);
}
public bool ContainsKey(TKey key)
{
return keys.Contains(key);
}
private bool TryGetIndex(TKey key, out int index)
{
return (index = keys.IndexOf(key)) != -1;
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
for (int i = 0; i < Count; i++)
yield return new KeyValuePair<TKey, TValue>(keys, values);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
ICollection IDictionary<TKey, TValue>.Keys*
* {
get { return keys; }
}
bool IDictionary<TKey, TValue>.Remove(TKey key)
{
return Remove(key);
}
ICollection IDictionary<TKey, TValue>.Values*
* {
get { return values; }
}
public void Add(KeyValuePair<TKey, TValue> item)
{
keys.Add(item.Key);
values.Add(item.Value);
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
return ContainsKey(item.Key);
}
public void CopyTo(KeyValuePair<TKey, TValue> array, int arrayIndex)
{
for (int i = arrayIndex; i < array.Length; i++)
{
array = new KeyValuePair<TKey, TValue>(keys, values);
}
}
public bool IsReadOnly*
* {
get { return false; }
}
public bool Remove(KeyValuePair<TKey, TValue> item)
{
return Remove(item.Key);
}
}
public static class KVPDictionaryExtensions*
* {
public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(this KVPList<TKey, TValue> d)
{
return RTHelper.CreateDictionary(d.Keys, d.Values);
}
public static KVPList<TKey, TValue> ToKVPList<TKey, TValue>(this IDictionary<TKey, TValue> d)
{
return new KVPList<TKey, TValue>(d.Keys.ToList(), d.Values.ToList());
}
}
Edit:
Sorry forgot to mention about RTHelper, it’s just a static utility class I use:
public static class RTHelper
{
///
/// Creates a dictionary out of the passed keys and values*
* ///
public static Dictionary<T, U> CreateDictionary<T, U>(IEnumerable keys, IList values)
{
return keys.Select((k, i) => new { k, v = values }).ToDictionary(x => x.k, x => x.v);
}
}
InBounds is just an extension method:
public static class IListExtensions
{
public static bool InBounds(this IList list, int i)
{
return i > -1 && i < list.Count;
}
}*
You could replace the code if you don’t want to add a helper and extensions static classes.
Don’t forget your usings:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;_