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 () {
	
	}
}

You can't serialize generic classes in Unity. Sorry.

–

@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 MapList this will be correctly serialized. [System.Serializable] public class MapListStringInt : MapList<string,int> {}

–

4 Answers

4

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
}

Great solution.

–

splendid, how did you find it out?

–

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.

–

@manu3d ;) Yeah, you're right sorry. I should have said that it will never serialize. You can display any data in the inspector with a custom script. Types with serialization support include basic data types, arrays, strings and List<>. You can also create your own with the System.Serializable attribute. However, this just means that its serializable members will be serializable. There is simply no support for Dictionary serialization at this stage. Just export it as a list and initialize the dictionary with it at the start of your game.

–

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 :wink:

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 :wink:

wow thanks for the info :) .. i didnt think that serializing a generic variable would take this much

–

+100000000000000 @GuyLSmith

–

@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."

–

@Bunny83 how the F*** they serialize ExposedReference<T>??? SerializedProperty serializedProperty; serializedProperty.exposedReferenceValue There are also in UnityEngine.Serialization DictionarySurrogate

–

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; }
}

Since I didn’t see anyone specifically mention it here you can inherit from a generic type that is a MonoBehavior also.

[Serializable]
public class MyGeneric<T> : MonoBehavior {
    public T Variable;
}

public class MyImplementtion : MyGeneric<int> {

}

As far as I can tell Variable will serialize as long as the type given is serializable.