Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
1
Question by hadicoco · Sep 22, 2014 at 05:10 PM · c#serializationdictionary

Better Dictionary? (serializable with more functionality)

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?

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

1 Reply

· Add your reply
  • Sort: 
avatar image
4
Best Answer

Answer by vexe · Sep 22, 2014 at 05:13 PM

[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[i]))
             AssertUniqueKey(value);
         keys[i] = value;
     }

     public TKey GetKeyAt(int i)
     {
         AssertIndexInBounds(i);
         return keys[i];
     }

     public void SetValueAt(int i, TValue value)
     {
         AssertIndexInBounds(i);
         values[i] = value;
     }

     public TValue GetValueAt(int i)
     {
         AssertIndexInBounds(i);
         return values[i];
     }

     public KeyValuePair<TKey, TValue> GetPairAt(int i)
     {
         AssertIndexInBounds(i);
         return new KeyValuePair<TKey, TValue>(keys[i], values[i]);
     }

     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[i], values[i]);
     }

     IEnumerator IEnumerable.GetEnumerator()
     {
         return GetEnumerator();
     }


     ICollection<TKey> IDictionary<TKey, TValue>.Keys
     {
         get { return keys; }
     }

     bool IDictionary<TKey, TValue>.Remove(TKey key)
     {
         return Remove(key);
     }

     ICollection<TValue> 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[i] = new KeyValuePair<TKey, TValue>(keys[i], values[i]);
         }
     }

     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
 {
         /// <summary>
         /// Creates a dictionary out of the passed keys and values
         /// </summary>
         public static Dictionary<T, U> CreateDictionary<T, U>(IEnumerable<T> keys, IList<U> values)
         {
             return keys.Select((k, i) => new { k, v = values[i] }).ToDictionary(x => x.k, x => x.v);
         }
 }

InBounds is just an extension method:

 public static class IListExtensions
 {
         public static bool InBounds<T>(this IList<T> 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;




Comment
Add comment · Show 14 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image hadicoco · Sep 22, 2014 at 05:25 PM 0
Share

thanks, but I'm getting some errors what is keys.InBounds? and what is RTHelper?

avatar image tw1st3d · Sep 22, 2014 at 05:26 PM 1
Share

Question converted to comment. Please use the "add new comment" button when replying to an answer.

avatar image vexe · Sep 22, 2014 at 05:28 PM 0
Share

@hadicoco see my latest edit. I just added those :)

avatar image hadicoco · Sep 22, 2014 at 05:30 PM 0
Share

thanks, that's exactly what I want. is there anyway to skip the subclassing phase?

avatar image vexe · Sep 22, 2014 at 05:32 PM 0
Share

With Unity's current serialization system? no... it has very poor serialization support for generics, you have to subclass :| - You could take a look at ShowEmAll, it provides better serialization for generics

Show more comments

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

3 People are following this question.

avatar image avatar image avatar image

Related Questions

Nested Dictionary data lost after playmode 0 Answers

From two lists into a dictionary... and back? 5 Answers

Serializing data structures the Unity way. Possible? 1 Answer

In this case the lists do not serialize. Why? 2 Answers

Multiple Cars not working 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges