I’m trying to use ISerializationCallbackReceiver to allow me to serialize/deserialize a HashSet to/from a List. However, I’m running into an issue where I can’t change the size (and thus, the contents) of the underlying List. Here’s some code, which I’ve tried in 5.0, 5.1, and 5.2.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class HashSetSerializer : MonoBehaviour, ISerializationCallbackReceiver {
[SerializeField]
private List<int> serializedField;
private HashSet<int> runtimeField;
#region ISerializationCallbackReceiver implementation
public void OnBeforeSerialize ()
{
this.serializedField = new List<int> (this.runtimeField);
Debug.Log ("HashSet script BeforeSerialize -- # of elements ser / runtime: " + this.serializedField.Count + "/ " + this.runtimeField.Count);
}
public void OnAfterDeserialize ()
{
this.runtimeField = new HashSet<int> (this.serializedField);
Debug.Log ("HashSet script AfterDeserialize -- # of elements ser / runtime: " + this.serializedField.Count + "/ " + this.runtimeField.Count);
}
#endregion
}
When I go into the editor, and change the size of the “serializedField” List from 0 to say, 5, the size instead clamps to 1 – the only sizes that take seem to be 0 and 1. Attempting to change the size does trigger deserialization though, which provides console output that reads: “HashSet script AfterDeserialize – # of elements ser / runtime: 5 / 1.” I get the same effect if I instead manually iterate over the List’s elements to populate the HashSet.
Here’s where it gets a bit stranger: If I create a different MonoBehaviour that changes the runtime field to a List, then it all works! Like this:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class ListSerializer : MonoBehaviour, ISerializationCallbackReceiver {
[SerializeField]
private List<int> serializedField;
private List<int> runtimeField;
#region ISerializationCallbackReceiver implementation
public void OnBeforeSerialize ()
{
this.serializedField = new List<int> (this.runtimeField);
Debug.Log ("HashSet script BeforeSerialize -- # of elements ser / runtime: " + this.serializedField.Count + "/ " + this.runtimeField.Count);
}
public void OnAfterDeserialize ()
{
this.runtimeField = new List<int> (this.serializedField);
Debug.Log ("HashSet script AfterDeserialize -- # of elements ser / runtime: " + this.serializedField.Count + "/ " + this.runtimeField.Count);
}
#endregion
}
Note that the UNDERLYING SERIALIZED TYPE in both cases is a List, which Unity can serialize – all that’s changed is the runtime type that we’re deserializing to, so this should have nothing to do with that, yet it somehow seems to. Note that replacing the HashSet with a Dictionary (another type that Unity can’t serialize) breaks in the same way that a HashSet does. You can confirm this by trying out the code that Unity provides as the ISerializationCallbackReceiver example, found here: Unity - Scripting API: ISerializationCallbackReceiver.OnBeforeSerialize. In that case, the underlying Lists are stuck with their initialized contents, and the size of the Lists can’t be changed.
For the HashSet example, changing the collection type from to that of a nullable, serializable object (like an Object tagged with [Serializable]), the maximum size of the serialized list becomes 0, instead of 1.
All told, there are 3 possibilities that I can think of right now:
- I’ve just done something incorrectly, but if that’s the case then Unity needs to update their example because that seems just as broken.
- This is a bug in Unity. Possible, given that the problem only seems to arise when working with types that Unity can’t serialize.
- This has to do with C#. The change in behavior between collections of non-nullable ints vs nullable Objects suggests that it might have to do with how C# deals with empty or empty-ish collections, but I’d expect normal behavior out of the List, which is NOT empty when resized (the default int is 0, not Null).
Anyways, I’d love to hear people’s thoughts on this. Are people out there getting ISerializationCallbackReceiver to work properly? Are you experiencing similar issues?