- Home /
Serializable class using generics
I have a problem with a recently modified class not showing up in the inspector.
I am attempting to make a class which I can use to automatically stitch up generic list of key/value pairs into a dictionary. However, I am unable to get the class to show up in the inspector when it is added to a behavior. This was working, until I modified the class to act more like a data container by modifying the logic to allow for the getData and setData methods.
I am including the new non-functioning code, as well as an example behavior that I was using to test.
[System.Serializable]
public class MapList<Key, Data>
{
//Should never be accessed directly in code
//but needs to be public for serialization
public List<Link> maps;
public Link nullLink;
private Dictionary<Key, Link> m_dict;
protected Dictionary<Key, Link> stitchedDict{
get{
if (
this.maps == null
|| this.m_dict == null
|| this.maps.Count != this.m_dict.Count
) {
restitchDict ();
}
return this.m_dict;
}
set{ this.m_dict = value; }
}
public Data getData (Key key)
{
if (this.stitchedDict.ContainsKey (key)) {
return this.stitchedDict[key].data;
} else {
return this.nullLink.data;
}
}
public void setData (Key _key, Data _data)
{
if (this.stitchedDict[_key] == null) {
Link dataMap = new Link ();
dataMap.key = _key;
dataMap.data = _data;
this.addMap (dataMap);
this.restitchDict ();
} else {
this.stitchedDict[_key].data = _data;
}
}
protected void restitchDict ()
{
if(this.m_dict == null){
this.m_dict = new Dictionary<Key, Link> ();
}
this.m_dict.Clear ();
this.validateMaps ();
foreach (Link nextMap in this.maps) {
this.m_dict[nextMap.key] = nextMap;
}
}
protected void addMap (Link _dataMap)
{
this.validateMaps ();
this.maps.Add (_dataMap);
}
protected void validateMaps ()
{
if (this.maps == null) {
this.maps = new List<Link> ();
}
}
//INTERNAL CLASSES
[System.Serializable]
public class Link
{
public Key key;
public Data data;
}
}
public class ExecBehavior : MonoBehaviour {
public MapList<string, int> exampleMap;
public List<string> compare1;
public List<int> compare2;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
@syclamoth , is there any specific reason for this? even i tried sometime back and failed in this
is there any specific reason for this
Lazyness? Its definetely not rocket since to serialize and recreate generic types. Did this before. Unity doesn't support correct aliasing either (which is much harder), so I guess the guy who wrote the serialization framework just isn't that good.
Anyway, you can drive around the problem by just subclassing from your generic like this:
// Unlike $$anonymous$$apList this will be correctly serialized.
[System.Serializable] public class $$anonymous$$apListStringInt : $$anonymous$$apList<string,int> {}
Answer by andrey_bryzgalov · Jul 26, 2012 at 04:20 PM
You can not serialize a class using the Unity serializer if it has fields with generics.
[Serializable]
public class GenericClass<T>
{
public T someField;
}
[Serializable]
public class SomeClass
{
public GenericClass<string> stringClassField; //does not serialize
}
However you can define subclasses for constructed generic types:
[Serializable]
public class GenericClassOfString : GenericClass<String>
{
}
[Serializable]
public class SomeClass
{
public GenericClassOfString stringClassField; //serializes
}
Hmmmm. I just tried to make this solution work for a Dictionary but to no avail. Did it perhaps work with previous versions of Unity? Can you show a fuller example, with both the base/child classes and a functioning, serializing inspector? It would be much appreciated.
@manu2d Dictionaries will never show up in the inspector, unfortunately.
The best you can do is to create a list of objects with a key and value and use it to initialize your dictionary.
This solution is for using your own defined generic types.
Hey, give me my third dimension back! I am manu3d, not 2d! ;)
Concerning Dictionaries not showing up in the inspector, this is not entirely correct. Of course it is correct that the default inspector is not capable of displaying dictionaries. However, I have an open question on the subject and the custom inspector I've posted correctly shows the content of the dictionary in the inspector. BUT... the content is deleted as soon as I switch from Edit to Game mode, a serialization problem by my understanding.
Also, I tried to use this solution in the context of the values of the dictionary being a custom type. But so far I can't even get a Dictionary[string,int] to serialize correctly, never $$anonymous$$d my own custom class.
Answer by Bunny83 · Feb 06, 2012 at 03:50 AM
Unity uses it's own serialization which doesn't support generic types because that would be the hell to implement. Even a class like this can't be serialized by Unity just because it has a generic parameter:
[System.Serializable]
public class TestClass<T>
{
public int number;
}
Furthermore you used your internal Link class in your List, but Link depends on one of your generic parameters. Now i dare you to write a serializer that can handle this setup ;)
Generics work way different to c++ templates where it's just a search&replace at compile time. Generics are compiled as they are. It's even hard to describe how they work ;)
wow thanks for the info :) .. i didnt think that serializing a generic variable would take this much
Well, it seems easy, but Unity doesn't do a string or xml serialization. Unity uses their own binary format, so you have to deter$$anonymous$$e the type of each item. In the OPs case above, what do you think is the typename of the internal class $$anonymous$$apList<int,float>.Link
?
The sort "Name" is just Link
which doesn't say anything about the used type or if the internal class is affected by any generic parameter or not.
The "FullName" looks like this:
$$anonymous$$apList`2+Link[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, Public$$anonymous$$eyToken=b77a5c561934e089],[System.Single, mscorlib, Version=2.0.0.0, Culture=neutral, Public$$anonymous$$eyToken=b77a5c561934e089]]
Now have fun to serialize the type of this class ;)
Nobody said that's impossible to create a general serializer, but generics allow really nasty constructs like generics in generics in generics. If you have to analyse this with reflection you have to give up at some point.
Bunny83 wrote: "because that would be the hell to implement"
Never an adequate excuse for failing to implement a basic feature. Shame on the Unity dev $$anonymous$$m!
@GuyLSmith Hit the nail right on the head. Serialization in Unity is a complete mess of a nightmare. Those of us paying good money for Pro should never have to hear excuses like "Eh..it's too hard, so we aren't going to bother. Deal with it."
Answer by vexe · Jan 04, 2015 at 11:11 AM
Generic type serialization alone with all the cases Unity doesn't support is what I offer in VFW. I make use of ISerializationCallbackReceiver and FullSerializer to write a better custom serialization system. Sick of the fact that Unity's not taking action towards this subject. While there are private individual solutions, they're either kept private or sold at the asset store. This stuff should be available to us out of the box let alone a free downloadable package.
In VFW you could go wild and even serialize nested generic types, so you could write (not that this is realistic, but just to give you a flavor of what you can do):
public class Test : BetterBehaviour
{
[Serialize] // makes it both serializable and thus exposed
private Dictionary<Tuple<string, int>, List<IOwner<GameObject>>> dictionary { get; set; }
}
public interface IOwner<T>
{
T Value { get; set; }
}