I´ve got this very simple implementation of a serializable hash set using ISerializationCallbackReceiver:
[Serializable]
public class SerializableHashSet<T> : HashSet<T>,ISerializationCallbackReceiver
{
[SerializeField] private List<T> elements = new List<T>();
// save the set to a list
public void OnBeforeSerialize()
{
elements.Clear();
elements.AddRange(this);
}
// load set from list
public void OnAfterDeserialize()
{
Clear();
UnionWith(elements);
}
}
However this is constantly throwing “index out of range” when going into play mode:
IndexOutOfRangeException: Array index is out of range.
System.Collections.Generic.HashSet1[T].Add (.T item) System.Collections.Generic.HashSet1[T].UnionWith (IEnumerable1 other) SerializableHashSet1[T].OnAfterDeserialize () (at Assets/Model/Cloth/SerializableHashSet.cs:22)
This seems to be a thread related issue, but I don`t have a clue what is exactly causing this. I only use one instance of this class in my whole project, which is instantiated in Awake(), and used in Update().
I`ve also tried a serializable dictionary implemented in a similar way (two Lists instead of one, to hold keys and values respectively) and exceptions are being thrown too.
Seems like a bug.This seems to be happening internally in HashSet.UnionWith (in a call to Add), but the docs states that only a ArgumentNullException can be thrown by the UnionWith method.
Have you tried iterating over the elements array, and adding the elements one by one?
Yes, in case of using a “foreach” instead, the same exception is thrown when reaching the “foreach” line.
Even using the example dictionary implementation provided in the unity docs (Unity - Scripting API: ISerializationCallbackReceiver.OnBeforeSerialize) throws like crazy. In the docs they do warn you that modifying the object that is being serialized can confuse the serializer, and in the c# docs they also warn you that funky stuff can happen when accessing a hash set/dictionary from several threads at once, which i think is the cause of this error since unity serialization happens on a different thread.
There must be a secret way of using ISerializationCallbackReceiver that i`m not aware of…
Well, after some more testing, ISerializationCallbackReceiver seems fundamentally broken to me. I´ve managed to reproduce this problem with a very simple setup, and it happens every single time.
I´ve issued a bug, will report here any news on the subject.
Thanks for issuing a bug! - I’ve been using a serializable dictionary very similar to this one here
What’s weird is that I’ve used this method for a couple of days and it was working just fine, but now out of no where I started getting these IndexOutOfRangeExceptions in the foreach (at the call to MoveNext to be exact)
Regarding dictionary, here’s what MSDN says:
So most likely we’re dealing with a threading issue.
I changed the relation to be composition instead of inheritance and it seems to be working (at least for now), i.e.
Lately I’ve been having issues with ISerializationCallbackReceiver in 5.2. I’m essentially unable to modify any lists that are fields of classes that implement the interface – the list will appear in the editor, but the size will always stay at 0, regardless of what I set it to (in some cases I think it would go to 1, but no further). This has been true for the following cases:
Serializable versions of non-serializable classes. In fact, I copied the SerializeableHashset code posted by the OP and this issue occurred.
Non-generic classes that inherit from classes mentioned in (1). For example an IntSerializableHashset : SerializableHashset {}
Classes that implement the interface in order to serialize a field, not “itself.” I’ve included some code for such a class, which should simply be packing/unpacking a dictionary to/from lists when serializing/deserializing.
[Serializable]
public class DictHaver : ISerializationCallbackReceiver {
[SerializeField]
private List<int> keys; // = new List<int> (); //initializing doesn't fix anything
[SerializeField]
private List<string> values; // = new List<string> ();
private Dictionary<int, string> d = new Dictionary<int, string>();
public Dictionary<int,string> D { get { return this.d; } }
public void OnBeforeSerialize(){
keys.Clear ();
values.Clear ();
foreach (KeyValuePair<int, string> pair in this.d) {
keys.Add (pair.Key);
values.Add (pair.Value);
}
}
public void OnAfterDeserialize(){
this.d.Clear ();
if (keys.Count != values.Count)
return;
for (int i = 0; i< keys.Count; i++)
this.d.Add (keys [i], values [i]);
}
}
I swear there was a time when such code worked as intended.
However, I am able to create a List that implements ISerializationCallbackReceiver and stores its data in an underlying list field. Go figure.
EDIT: Typically I use Vexe’s VFW, and had the same issue with HashSets, but not Dictionaries. Looking through the Vexe code, I didn’t find a FullSerializer for HashSet. Maybe that’s the Vexe issue? (Just an aside, the rest of the post is about problems in vanilla Unity).
Hi! Any update on this? I need a hashset to be visible in the inspector and updated with SetDirty, which is what the serializable thing you added to it, should do, but it doesn’t. can’t see it in the inspector and when I go to Play, its cleared Please help senpai