-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSerializedDictionary.cs
157 lines (137 loc) · 4.18 KB
/
SerializedDictionary.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
/// <summary>
/// Source: https://github.com/upscalebaby/generic-serializable-dictionary
/// Generic Serializable Dictionary for Unity 2020.1 and above.
/// Simply declare your key/value types and you're good to go - zero boilerplate.
/// </summary>
namespace razveck.UnityUtility {
[Serializable]
public class SerializedDictionary<TKey, TValue> : IDictionary<TKey, TValue>, ISerializationCallbackReceiver {
// Internal
[SerializeField]
private List<KeyValuePair> list = new List<KeyValuePair>();
[SerializeField, HideInInspector]
private Dictionary<TKey, int> indexByKey = new Dictionary<TKey, int>();
[SerializeField, HideInInspector]
private Dictionary<TKey, TValue> dict = new Dictionary<TKey, TValue>();
#pragma warning disable 0414
[SerializeField, HideInInspector]
private bool keyCollision;
#pragma warning restore 0414
[Serializable]
private struct KeyValuePair {
public TKey Key;
public TValue Value;
public KeyValuePair(TKey Key, TValue Value) {
this.Key = Key;
this.Value = Value;
}
}
// Lists are serialized natively by Unity, no custom implementation needed.
public void OnBeforeSerialize() { }
// Populate dictionary with pairs from list and flag key-collisions.
public void OnAfterDeserialize() {
dict.Clear();
indexByKey.Clear();
keyCollision = false;
for(int i = 0; i < list.Count; i++) {
var key = list[i].Key;
if(key != null && !ContainsKey(key)) {
dict.Add(key, list[i].Value);
indexByKey.Add(key, i);
} else {
keyCollision = true;
}
}
}
// IDictionary
public TValue this[TKey key]
{
get => dict[key];
set
{
dict[key] = value;
if(indexByKey.ContainsKey(key)) {
var index = indexByKey[key];
list[index] = new KeyValuePair(key, value);
} else {
list.Add(new KeyValuePair(key, value));
indexByKey.Add(key, list.Count - 1);
}
}
}
public ICollection<TKey> Keys => dict.Keys;
public ICollection<TValue> Values => dict.Values;
public void Add(TKey key, TValue value) {
dict.Add(key, value);
list.Add(new KeyValuePair(key, value));
indexByKey.Add(key, list.Count - 1);
}
public bool ContainsKey(TKey key) => dict.ContainsKey(key);
public bool Remove(TKey key) {
if(dict.Remove(key)) {
var index = indexByKey[key];
list.RemoveAt(index);
UpdateIndexLookup(index);
indexByKey.Remove(key);
return true;
} else {
return false;
}
}
private void UpdateIndexLookup(int removedIndex) {
for(int i = removedIndex; i < list.Count; i++) {
var key = list[i].Key;
indexByKey[key]--;
}
}
public bool TryGetValue(TKey key, out TValue value) => dict.TryGetValue(key, out value);
// ICollection
public int Count => dict.Count;
public bool IsReadOnly { get; set; }
public void Add(KeyValuePair<TKey, TValue> pair) {
Add(pair.Key, pair.Value);
}
public void Clear() {
dict.Clear();
list.Clear();
indexByKey.Clear();
}
public bool Contains(KeyValuePair<TKey, TValue> pair) {
TValue value;
if(dict.TryGetValue(pair.Key, out value)) {
return EqualityComparer<TValue>.Default.Equals(value, pair.Value);
} else {
return false;
}
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) {
if(array == null)
throw new ArgumentException("The array cannot be null.");
if(arrayIndex < 0)
throw new ArgumentOutOfRangeException("The starting array index cannot be negative.");
if(array.Length - arrayIndex < dict.Count)
throw new ArgumentException("The destination array has fewer elements than the collection.");
foreach(var pair in dict) {
array[arrayIndex] = pair;
arrayIndex++;
}
}
public bool Remove(KeyValuePair<TKey, TValue> pair) {
TValue value;
if(dict.TryGetValue(pair.Key, out value)) {
bool valueMatch = EqualityComparer<TValue>.Default.Equals(value, pair.Value);
if(valueMatch) {
return Remove(pair.Key);
}
}
return false;
}
// IEnumerable
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => dict.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => dict.GetEnumerator();
}
}