Skip to content

Instantly share code, notes, and snippets.

@Seneral
Last active July 7, 2021 14:01
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save Seneral/bc966cd03095946d9e635ebd89fadfac to your computer and use it in GitHub Desktop.
Save Seneral/bc966cd03095946d9e635ebd89fadfac to your computer and use it in GitHub Desktop.
Fully capable SerializableAction for Unity. Supports targets of both UnityEngine.Object and System.Object and one-layer serialization of unserializable types. Supports static and generic methods and classes; Supports most anonymous actions, fully capable of using the context. Support: forum.unity3d.com/threads/406299; Check for updates: https://…
namespace SerializableActionHelper
{
using UnityEngine;
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Reflection;
/// <summary>
/// Wrapper for a single-cast Action without parameters that handles serialization supporting System- and UnityEngine Objects aswell as anonymous methods to some degree
/// </summary>
[Serializable]
public class SerializableAction
{
private Action action;
[SerializeField]
private SerializableObject serializedTarget;
[SerializeField]
private SerializableMethodInfo serializedMethod;
// Accessor Properties
public Action Action
{
get
{
if (action == null)
action = DeserializeAction ();
return action;
}
}
public object Target { get { return Action.Target; } }
public MethodInfo Method { get { return Action.Method; } }
/// <summary>
/// Create a new SerializableAction from a non-anonymous action (System or Unity)
/// </summary>
public SerializableAction (Action srcAction)
{
if (srcAction.GetInvocationList ().Length > 1)
throw new UnityException ("Cannot create SerializableAction from a multi-cast action!");
SerializeAction (srcAction);
}
#region General
/// <summary>
/// Invoke this serialized action
/// </summary>
public void Invoke ()
{
if (Action != null)
Action.Invoke();
}
/// <summary>
/// Returns whether this action is valid
/// </summary>
public bool IsValid ()
{
if (action == null)
{
try
{
action = DeserializeAction ();
}
catch
{
return false;
}
}
return true;
}
#endregion
#region Serialization
/// <summary>
/// Serializes the given action depending on the type (System or Unity) and stores it into this SerializableAction
/// </summary>
private void SerializeAction (Action srcAction)
{
action = srcAction;
//Debug.Log ("Serializing action for method '" + srcAction.Method.Name + "'!"); // TODO: DEBUG REMOVE
serializedMethod = new SerializableMethodInfo (srcAction.Method);
serializedTarget = new SerializableObject (action.Target);
}
/// <summary>
/// Deserializes the action depending on the type (System or Unity) and returns it
/// </summary>
private Action DeserializeAction ()
{
// Target
object target = serializedTarget.Object;
// Method
MethodInfo method = serializedMethod.methodInfo;
if (method == null)
throw new DataMisalignedException ("Could not deserialize action method '" + serializedMethod.SignatureName + "'!");
return Delegate.CreateDelegate (typeof(Action), target, method) as Action;
}
#endregion
}
}
using UnityEngine;
using UnityEditor;
using System;
using System.Collections.Generic;
using SerializableActionHelper;
public class SerializableActionTestWindow : EditorWindow
{
[MenuItem ("Window/SerializableAction Test")]
private static void Open ()
{
EditorWindow.GetWindow<SerializableActionTestWindow> ("SerializableAction Test");
}
#region Test Methods: Unity
private void UnityTargettedNormal ()
{
Debug.Log ("Unity-Targetted Normal executed!");
}
private void UnityTargettedGenericMethodNormal<T> ()
{
Debug.Log ("Unity-Targetted GenericMethod<" + typeof(T).Name + "> Normal executed!");
}
private static void UnityTargettedStatic ()
{
Debug.Log ("Unity-Targetted Static executed!");
}
private static void UnityTargettedGenericMethodStatic<T> ()
{
Debug.Log ("Unity-Targetted GenericMethod<" + typeof(T).Name + "> Static executed!");
}
private static Action getUnityTargettedAnonymous ()
{
return new Action (() => Debug.Log ("Unity-Targetted Anonymous executed!"));
}
private static Action getUnityTargettedAnonymous<T> ()
{
return new Action (() => Debug.Log ("Unity-Targetted GenericMethod<" + typeof(T).Name + "> Anonymous executed!"));
}
#endregion
#region Test Methods: System
[Serializable]
public class SystemClass : System.Object
{
public void SystemTargettedNormal ()
{
Debug.Log ("System-Targetted Normal executed!");
}
public void SystemTargettedGenericMethodNormal<T> ()
{
Debug.Log ("System-Targetted GenericMethod<" + typeof(T).Name + "> Normal executed!");
}
public static void SystemTargettedStatic ()
{
Debug.Log ("System-Targetted Static executed!");
}
public static void SystemTargettedGenericMethodStatic<T> ()
{
Debug.Log ("System-Targetted GenericMethod<" + typeof(T).Name + "> Static executed!");
}
public Action getSystemTargettedAnonymous ()
{
return new Action (() => Debug.Log ("System-Targetted Anonymous executed!"));
}
public Action getSystemTargettedGenericAnonymous<T> ()
{
return new Action (() => Debug.Log ("System-Targetted GenericMethod<" + typeof(T).Name + "> Anonymous executed!"));
}
}
#endregion
#region Test Methods: System (Generic Class)
[Serializable]
public class SystemGenericTypeClass<T> : System.Object
{
public void SystemTargettedGenericNormal ()
{
Debug.Log ("System-Targetted GenericClass<" + typeof(T).Name + "> Normal executed!");
}
public static void SystemTargettedGenericStatic ()
{
Debug.Log ("System-Targetted GenericClass<" + typeof(T).Name + "> Static executed!");
}
public Action getSystemTargettedGenericAnonymous ()
{
return new Action (() => Debug.Log ("System-Targetted GenericClass<" + typeof(T).Name + "> Anonymous executed!"));
}
}
#endregion
public SystemClass systemClass = new SystemClass ();
public SystemGenericTypeClass<Vector3> systemGenericTypeClass = new SystemGenericTypeClass<Vector3> ();
public int testInt = 62;
public SerializableAction unityStaticAction;
public SerializableAction unityGenericMethodStaticAction;
public SerializableAction systemStaticAction;
public SerializableAction systemGenericStaticAction;
public SerializableAction systemGenericMethodStaticAction;
public SerializableAction unityNormalAction;
public SerializableAction unityGenericMethodNormalAction;
public SerializableAction systemNormalAction;
public SerializableAction systemGenericNormalAction;
public SerializableAction systemGenericMethodNormalAction;
public SerializableAction unityAnonymousAction;
public SerializableAction unityGenericMethodAnonymousAction;
public SerializableAction unityLocalVarAnonymousAction;
public SerializableAction unityClassVarAnonymousAction;
public SerializableAction systemAnonymousAction;
public SerializableAction systemGenericAnonymousAction;
public SerializableAction systemGenericMethodAnonymousAction;
public void OnGUI ()
{
// -----
EditorGUILayout.Space ();
GUILayout.Label ("STATIC");
EditorGUILayout.Space ();
ActionGUI ("Unity-Targetted Static", ref unityStaticAction, SerializableActionTestWindow.UnityTargettedStatic);
EditorGUILayout.Space ();
ActionGUI ("Unity-Targetted GenericMethod<Vector3> Static", ref unityGenericMethodStaticAction, SerializableActionTestWindow.UnityTargettedGenericMethodStatic<Vector3>);
EditorGUILayout.Space ();
ActionGUI ("System-Targetted Static", ref systemStaticAction, SystemClass.SystemTargettedStatic);
EditorGUILayout.Space ();
ActionGUI ("System-Targetted GenericClass<Vector3> Static", ref systemGenericStaticAction, SystemGenericTypeClass<Vector3>.SystemTargettedGenericStatic);
EditorGUILayout.Space ();
ActionGUI ("System-Targetted GenericMethod<Vector3> Static", ref systemGenericMethodStaticAction, SystemClass.SystemTargettedGenericMethodStatic<Vector3>);
// -----
EditorGUILayout.Space ();
GUILayout.Label ("NORMAL");
EditorGUILayout.Space ();
ActionGUI ("Unity-Targetted Normal", ref unityNormalAction, this.UnityTargettedNormal);
EditorGUILayout.Space ();
ActionGUI ("Unity-Targetted GenericMethod<Vector3> Normal", ref unityGenericMethodNormalAction, this.UnityTargettedGenericMethodNormal<Vector3>);
EditorGUILayout.Space ();
ActionGUI ("System-Targetted Normal", ref systemNormalAction, systemClass.SystemTargettedNormal);
EditorGUILayout.Space ();
ActionGUI ("System-Targetted GenericClass<Vector3> Normal", ref systemGenericNormalAction, systemGenericTypeClass.SystemTargettedGenericNormal);
EditorGUILayout.Space ();
ActionGUI ("System-Targetted GenericMethod<Vector3> Normal", ref systemGenericMethodNormalAction, systemClass.SystemTargettedGenericMethodNormal<Vector3>);
// -----
EditorGUILayout.Space ();
GUILayout.Label ("ANONYMOUS");
EditorGUILayout.Space ();
ActionGUI ("Unity-Targetted Anyonymous", ref unityAnonymousAction, getUnityTargettedAnonymous ());
EditorGUILayout.Space ();
testInt = EditorGUILayout.IntSlider ("Test Int", testInt, 0, 100);
ActionGUI ("Unity-Targetted ClassVar Anyonymous", ref unityClassVarAnonymousAction, () => Debug.Log ("Unity-Targetted ClassVar Anyonymous executed: " + testInt));
int localInt = testInt;
ActionGUI ("Unity-Targetted LocalVar Anyonymous", ref unityLocalVarAnonymousAction, () => Debug.Log ("Unity-Targetted LocalVar Anyonymous executed: " + localInt));
EditorGUILayout.Space ();
ActionGUI ("Unity-Targetted GenericMethod<Vector3> Anyonymous", ref unityGenericMethodAnonymousAction, getUnityTargettedAnonymous<Vector3> ());
EditorGUILayout.Space ();
ActionGUI ("System-Targetted Anyonymous", ref systemAnonymousAction, systemClass.getSystemTargettedAnonymous ());
EditorGUILayout.Space ();
ActionGUI ("System-Targetted GenericClass<Vector3> Anyonymous", ref systemGenericAnonymousAction, systemGenericTypeClass.getSystemTargettedGenericAnonymous ());
EditorGUILayout.Space ();
ActionGUI ("System-Targetted GenericMethod<Vector3> Anyonymous", ref systemGenericMethodAnonymousAction, systemClass.getSystemTargettedGenericAnonymous<Vector3> ());
// -----
Repaint ();
}
private void ActionGUI (string label, ref SerializableAction serializedAction, Action action)
{
GUILayout.Label (label + " SerializableAction");
if (serializedAction != null && !serializedAction.IsValid ())
serializedAction = null;
GUILayout.BeginHorizontal ();
if (GUILayout.Button ("Create (" + (serializedAction != null) + ")"))
{
serializedAction = new SerializableAction (action);
serializedAction.Invoke ();
}
if (GUILayout.Button ("Delete"))
{
serializedAction = null;
}
if (GUILayout.Button ("Invoke"))
{
if (serializedAction != null)
serializedAction.Invoke ();
else
Debug.LogError (label + " Action is null!");
}
GUILayout.EndHorizontal ();
}
}
namespace SerializableActionHelper
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using System.Runtime.CompilerServices;
/// <summary>
/// Wrapper for MethodInfo that handles serialization.
/// Stores declaringType, methodName, parameters and flags only and supports generic types (one level for class, two levels for method).
/// </summary>
[Serializable]
public class SerializableMethodInfo
{
private MethodInfo _methodInfo;
public MethodInfo methodInfo
{
get
{
if (_methodInfo == null)
Deserialize();
return _methodInfo;
}
}
[SerializeField]
private SerializableType declaringType;
[SerializeField]
private string methodName;
[SerializeField]
private List<SerializableType> parameters = null;
[SerializeField]
private List<SerializableType> genericTypes = null;
[SerializeField]
private int flags = 0;
// Accessors
public string SignatureName { get { return (((BindingFlags)flags&BindingFlags.Public) != 0? "public" : "private") + (((BindingFlags)flags&BindingFlags.Static) != 0? " static" : "") + " " + methodName; } }
public bool IsAnonymous { get { return Attribute.GetCustomAttribute (methodInfo, typeof(CompilerGeneratedAttribute), false) != null || declaringType.isCompilerGenerated; } }
public SerializableMethodInfo (MethodInfo MethodInfo)
{
_methodInfo = MethodInfo;
Serialize();
}
#region Serialization
public void Serialize()
{
if (_methodInfo == null)
return;
declaringType = new SerializableType (_methodInfo.DeclaringType);
methodName = _methodInfo.Name;
// Flags
if (_methodInfo.IsPrivate)
flags |= (int)BindingFlags.NonPublic;
else
flags |= (int)BindingFlags.Public;
if (_methodInfo.IsStatic)
flags |= (int)BindingFlags.Static;
else
flags |= (int)BindingFlags.Instance;
// Parameter
ParameterInfo[] param = _methodInfo.GetParameters ();
if (param != null && param.Length > 0)
parameters = param.Select ((ParameterInfo p) => new SerializableType (p.ParameterType)).ToList ();
else
parameters = null;
// Generic types
if (_methodInfo.IsGenericMethod)
{
methodName = _methodInfo.GetGenericMethodDefinition ().Name;
genericTypes = _methodInfo.GetGenericArguments ().Select ((Type genArgT) => new SerializableType (genArgT)).ToList ();
}
else
genericTypes = null;
}
public void Deserialize()
{
if (declaringType == null || declaringType.type == null || string.IsNullOrEmpty (methodName))
return;
// Parameters
Type[] param;
if (parameters != null && parameters.Count > 0) // With parameters
param = parameters.Select ((SerializableType t) => t.type).ToArray ();
else
param = new Type[0];
_methodInfo = declaringType.type.GetMethod (methodName, (BindingFlags)flags, null, param, null);
if (_methodInfo == null)
{ // Retry with private flags, because in some compiler generated methods flags will be uncertain (?) which then return public but are private
_methodInfo = declaringType.type.GetMethod (methodName, (BindingFlags)flags | BindingFlags.NonPublic, null, param, null);
if (_methodInfo == null)
throw new Exception ("Could not deserialize '" + SignatureName + "' in declaring type '" + declaringType.type.FullName + "'!");
}
if (_methodInfo.IsGenericMethodDefinition && genericTypes != null && genericTypes.Count > 0)
{ // Generic Method
Type[] genArgs = genericTypes.Select ((SerializableType t) => t.type).ToArray ();
MethodInfo genMethod = _methodInfo.MakeGenericMethod (genArgs);
if (genMethod != null)
_methodInfo = genMethod;
else
Debug.LogError ("Could not make generic-method definition '" + methodName + "' generic!");
}
}
#endregion
}
}
namespace SerializableActionHelper
{
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Reflection;
/// <summary>
/// Wrapper for an arbitrary object that handles basic serialization, both System.Object, UnityEngine.Object, and even basic unserializable types (the same way, but one-level only, unserializable members will be default or null if previously null)
/// </summary>
[Serializable]
public class SerializableObject : SerializableObjectOneLevel
{
[SerializeField]
private List<SerializableObjectTwoLevel> manuallySerializedMembers;
[SerializeField]
protected List<SerializableObjectTwoLevel> collectionObjects;
/// <summary>
/// Create a new SerializableObject from an arbitrary object
/// </summary>
public SerializableObject(object srcObject) : base(srcObject) { }
/// <summary>
/// Create a new SerializableObject from an arbitrary object with the specified name
/// </summary>
public SerializableObject(object srcObject, string name) : base(srcObject, name) { }
#region Serialization
/// <summary>
/// Serializes the given object and stores it into this SerializableObject
/// </summary>
protected override void Serialize()
{
if (isNullObject = _object == null)
return;
base.Serialize(); // Serialized normally
if (_object.GetType().IsGenericType &&
typeof(ICollection<>).MakeGenericType(_object.GetType().GetGenericArguments()).IsAssignableFrom(_object.GetType()))
{
IEnumerable collection = _object as IEnumerable;
collectionObjects = new List<SerializableObjectTwoLevel>();
foreach (object obj in collection)
collectionObjects.Add(new SerializableObjectTwoLevel(obj));
}
else if (typeof(UnityEngine.Object).IsAssignableFrom(_object.GetType())) { }
else if (_object.GetType().IsSerializable) { }
else
{ // Object is unserializable so it will later be recreated from the type, now serialize the serializable field values of the object
FieldInfo[] fields = objectType.type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
manuallySerializedMembers = new List<SerializableObjectTwoLevel>();
foreach (FieldInfo field in fields)
manuallySerializedMembers.Add(new SerializableObjectTwoLevel(field.GetValue(_object), field.Name));
//manuallySerializedMembers = fields.Select ((FieldInfo field) => new SerializableObjectOneLevel (field.GetValue (_object), field.Name)).ToList ();
}
}
/// <summary>
/// Deserializes this SerializableObject
/// </summary>
protected override void Deserialize()
{
if (isNullObject)
return;
base.Deserialize(); // Deserialize normally
Type type = objectType.type;
if (type.IsGenericType &&
typeof(ICollection<>).MakeGenericType(type.GetGenericArguments()).IsAssignableFrom(type))
{
if (collectionObjects != null && collectionObjects.Count > 0)
{ // Add deserialized objects to collection
MethodInfo add = type.GetMethod("Add");
foreach (SerializableObjectTwoLevel obj in collectionObjects)
add.Invoke(_object, new object[] { obj.Object });
}
}
else if (typeof(UnityEngine.Object).IsAssignableFrom(type))
_object = unityObject;
else if (type.IsSerializable)
_object = DeserializeFromString<System.Object>(serializedSystemObject);
else if (manuallySerializedMembers != null && manuallySerializedMembers.Count > 0)
{ // This object is an unserializable type, and previously the object was recreated from that type
// Now, restore the serialized field values of the object
FieldInfo[] fields = objectType.type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (fields.Length != manuallySerializedMembers.Count)
Debug.LogError("Field length and serialized member length doesn't match (" + fields.Length + ":" + manuallySerializedMembers.Count + ") for object " + objectType.type.Name + "!");
foreach (FieldInfo field in fields)
{
SerializableObjectTwoLevel matchObj = manuallySerializedMembers.Find((SerializableObjectTwoLevel obj) => obj.Name == field.Name);
if (matchObj != null)
{
object obj = null;
if (matchObj.Object == null) { }
else if (!field.FieldType.IsAssignableFrom(matchObj.Object.GetType()))
Debug.LogWarning("Deserialized object type " + matchObj.Object.GetType().Name + " is incompatible to field type " + field.FieldType.Name + "!");
else
obj = matchObj.Object;
field.SetValue(Object, obj);
}
else
Debug.LogWarning("Couldn't find a matching serialized field for '" + (field.IsPublic ? "public" : "private") + (field.IsStatic ? " static" : "") + " " + field.FieldType.FullName + "'!");
}
}
}
#endregion
}
/// <summary>
/// Wrapper for an arbitrary object that handles basic serialization, both System.Object, UnityEngine.Object, and even basic unserializable types (the same way, but one-level only, unserializable members will be default or null if previously null)
/// </summary>
[Serializable]
public class SerializableObjectTwoLevel : SerializableObjectOneLevel
{
[SerializeField]
private List<SerializableObjectOneLevel> manuallySerializedMembers;
[SerializeField]
protected List<SerializableObjectOneLevel> collectionObjects;
/// <summary>
/// Create a new SerializableObject from an arbitrary object
/// </summary>
public SerializableObjectTwoLevel (object srcObject) : base (srcObject) { }
/// <summary>
/// Create a new SerializableObject from an arbitrary object with the specified name
/// </summary>
public SerializableObjectTwoLevel (object srcObject, string name) : base(srcObject, name) { }
#region Serialization
/// <summary>
/// Serializes the given object and stores it into this SerializableObject
/// </summary>
protected override void Serialize ()
{
if (isNullObject = _object == null)
return;
base.Serialize (); // Serialized normally
if (_object.GetType().IsGenericType &&
typeof(ICollection<>).MakeGenericType(_object.GetType().GetGenericArguments()).IsAssignableFrom(_object.GetType()))
{
IEnumerable collection = _object as IEnumerable;
collectionObjects = new List<SerializableObjectOneLevel>();
foreach (object obj in collection)
collectionObjects.Add(new SerializableObjectOneLevel(obj));
}
else if (typeof(UnityEngine.Object).IsAssignableFrom(_object.GetType())) { }
else if (_object.GetType().IsSerializable) { }
else
{ // Object is unserializable so it will later be recreated from the type, now serialize the serializable field values of the object
FieldInfo[] fields = objectType.type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
manuallySerializedMembers = new List<SerializableObjectOneLevel>();
foreach (FieldInfo field in fields)
manuallySerializedMembers.Add(new SerializableObjectOneLevel(field.GetValue(_object), field.Name));
//manuallySerializedMembers = fields.Select ((FieldInfo field) => new SerializableObjectOneLevel (field.GetValue (_object), field.Name)).ToList ();
}
}
/// <summary>
/// Deserializes this SerializableObject
/// </summary>
protected override void Deserialize ()
{
if (isNullObject)
return;
base.Deserialize (); // Deserialize normally
Type type = objectType.type;
if (type.IsGenericType &&
typeof(ICollection<>).MakeGenericType(type.GetGenericArguments()).IsAssignableFrom(type))
{
if (collectionObjects != null && collectionObjects.Count > 0)
{ // Add deserialized objects to collection
MethodInfo add = type.GetMethod("Add");
foreach (SerializableObjectOneLevel obj in collectionObjects)
add.Invoke(_object, new object[] { obj.Object });
}
}
else if (typeof(UnityEngine.Object).IsAssignableFrom(type))
_object = unityObject;
else if (type.IsSerializable)
_object = DeserializeFromString<System.Object>(serializedSystemObject);
else if (manuallySerializedMembers != null && manuallySerializedMembers.Count > 0)
{ // This object is an unserializable type, and previously the object was recreated from that type
// Now, restore the serialized field values of the object
FieldInfo[] fields = objectType.type.GetFields (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (fields.Length != manuallySerializedMembers.Count)
Debug.LogError ("Field length and serialized member length doesn't match (" + fields.Length + ":" + manuallySerializedMembers.Count + ") for object " + objectType.type.Name + "!");
foreach (FieldInfo field in fields)
{
SerializableObjectOneLevel matchObj = manuallySerializedMembers.Find ((SerializableObjectOneLevel obj) => obj.Name == field.Name);
if (matchObj != null)
{
object obj = null;
if (matchObj.Object == null) { }
else if (!field.FieldType.IsAssignableFrom(matchObj.Object.GetType()))
Debug.LogWarning("Deserialized object type " + matchObj.Object.GetType().Name + " is incompatible to field type " + field.FieldType.Name + "!");
else
obj = matchObj.Object;
field.SetValue(Object, obj);
}
else
Debug.LogWarning("Couldn't find a matching serialized field for '" + (field.IsPublic ? "public" : "private") + (field.IsStatic ? " static" : "") + " " + field.FieldType.FullName + "'!");
}
}
}
#endregion
}
/// <summary>
/// Wrapper for an arbitrary object that handles basic serialization, both System.Object, UnityEngine.Object; unserializable types will be default or null if previously null;
/// NO RECOMMENDED TO USE, it is primarily built to support SerializableObject!
/// </summary>
[Serializable]
public class SerializableObjectOneLevel
{
[SerializeField]
public string Name; // Just to identify this object
protected object _object;
public object Object
{
get
{
if (_object == null)
Deserialize();
return _object;
}
}
// Serialized Data
[SerializeField]
protected bool isNullObject;
[SerializeField]
protected SerializableType objectType;
[SerializeField]
protected UnityEngine.Object unityObject;
[SerializeField]
protected string serializedSystemObject;
public SerializableObjectOneLevel (object srcObject)
{
_object = srcObject;
Serialize();
}
public SerializableObjectOneLevel(object srcObject, string name)
{
_object = srcObject;
Name = name;
Serialize();
}
#region Serialization
/// <summary>
/// Serializes the given object and stores it into this SerializableObject
/// </summary>
protected virtual void Serialize ()
{
if (isNullObject = _object == null)
return;
unityObject = null;
serializedSystemObject = String.Empty;
objectType = new SerializableType (_object.GetType ());
if (_object.GetType().IsGenericType &&
typeof(ICollection<>).MakeGenericType(_object.GetType().GetGenericArguments()).IsAssignableFrom(_object.GetType()))
{ // If levels are free to serialize, then they will get serialized, if not, then not
}
else if (typeof(UnityEngine.Object).IsAssignableFrom(_object.GetType()))
{
unityObject = (UnityEngine.Object)_object;
}
else if (_object.GetType().IsSerializable)
{
serializedSystemObject = SerializeToString<System.Object>(_object);
if (serializedSystemObject == null)
Debug.LogWarning("Failed to serialize field name " + Name + "!");
}
// else default object (and even serializable members) will be restored from the type
}
/// <summary>
/// Deserializes this SerializableObject
/// </summary>
protected virtual void Deserialize ()
{
_object = null;
if (isNullObject)
return;
if (objectType.type == null)
throw new Exception("Could not deserialize object as it's type could no be deserialized!");
Type type = objectType.type;
if (type.IsGenericType &&
typeof(ICollection<>).MakeGenericType(type.GetGenericArguments()).IsAssignableFrom(type))
{ // Collection type, if still more levels free, members will be serialized, if not, then not
_object = Activator.CreateInstance(type);
}
else if (typeof(UnityEngine.Object).IsAssignableFrom(type))
_object = unityObject;
else if (type.IsSerializable)
_object = DeserializeFromString<System.Object>(serializedSystemObject);
else
{ // Unserializable type, it will be recreated from the type (and even it's serializable members)
_object = Activator.CreateInstance(type);
}
// Not always critical. Can happen if GC or Unity deleted those references
// if (_object == null)
// Debug.LogWarning ("Could not deserialize object of type '" + type.Name + "'!");
}
#endregion
#region Embedded Util
/// <summary>
/// Serializes 'value' to a string, using BinaryFormatter
/// </summary>
protected static string SerializeToString<T> (T value)
{
if (value == null)
return null;
try
{
using (MemoryStream stream = new MemoryStream())
{
new BinaryFormatter().Serialize(stream, value);
stream.Flush();
return Convert.ToBase64String(stream.ToArray());
}
}
catch (System.Runtime.Serialization.SerializationException)
{
Debug.LogWarning("Failed to serialize " + value.GetType().ToString());
return null;
}
}
/// <summary>
/// Deserializes an object of type T from the string 'data'
/// </summary>
protected static T DeserializeFromString<T> (string data)
{
if (String.IsNullOrEmpty (data))
return default(T);
byte[] bytes = Convert.FromBase64String (data);
using (MemoryStream stream = new MemoryStream(bytes))
{
return (T)new BinaryFormatter().Deserialize (stream);
}
}
#endregion
}
}
namespace SerializableActionHelper
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using UnityEngine;
/// <summary>
/// Wrapper for System.Type that handles serialization.
/// Serialized Data contains assembly type name and generic arguments (one level) only.
/// </summary>
[System.Serializable]
public class SerializableType
{
public Type _type;
public Type type
{
get
{
if (_type == null)
Deserialize();
return _type;
}
}
[SerializeField]
private string typeName;
[SerializeField]
private string[] genericTypes;
public bool isCompilerGenerated { get { return Attribute.GetCustomAttribute (type, typeof(CompilerGeneratedAttribute), false) != null; } }
public SerializableType (Type Type)
{
_type = Type;
Serialize();
}
#region Serialization
public void Serialize ()
{
if (_type == null)
{
typeName = String.Empty;
genericTypes = null;
return;
}
if (_type.IsGenericType)
{ // Generic type
typeName = _type.GetGenericTypeDefinition ().AssemblyQualifiedName;
genericTypes = _type.GetGenericArguments ().Select ((Type t) => t.AssemblyQualifiedName).ToArray ();
}
else
{ // Normal type
typeName = _type.AssemblyQualifiedName;
genericTypes = null;
}
}
public void Deserialize ()
{
if (String.IsNullOrEmpty (typeName))
return;
_type = Type.GetType (typeName);
if (_type == null)
throw new Exception ("Could not deserialize type '" + typeName + "'!");
if (_type.IsGenericTypeDefinition && genericTypes != null && genericTypes.Length > 0)
{ // Generic type
Type[] genArgs = new Type[genericTypes.Length];
for (int i = 0; i < genericTypes.Length; i++)
genArgs[i] = Type.GetType (genericTypes[i]);
Type genType = _type.MakeGenericType (genArgs);
if (genType != null)
_type = genType;
else
Debug.LogError ("Could not make generic-type definition '" + typeName + "' generic!");
}
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment