Skip to content

[GenericSerializeReference] attribute to serialize generic property in Unity3D

License

Notifications You must be signed in to change notification settings

quabug/GenericSerializeReference

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

52 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Similar like SerializeReference but works on generic property.

public interface IMyInterface<T> {}
public class MyIntObject : IMyInterface<int> {}
public class MyGenericObject<T> : IMyInterface<T> {}

public class MyMonoBehavior : MonoBehaviour
{
    [GenericSerializeReference]
    public IMyInterface<int> Value { get; set; }
}

image

Requirement

Unity3D 2020.2 (not guaranteed to work below 2020.2)

Installation

OpenUPM: openupm add com.quabug.generic-serialize-reference

Limitations

  • Not support struct type.
  • Not support generic field.
  • Not support variance.

Costs

  • Extra time to generate IL code while building assembly
  • Extra memory space to store a generated field for each property.

How it works

AssemblyCSharp Mode

Either generate derived classes into a custom assembly named GenericSerializeReference.OverrideAssemblyCSharp or AssemblyCSharp.dll if there's no such custom assembly.

// Generate derived types into AssemblyCSharp.dll
public class MyMonoBehavior : MonoBehaviour
{
    // [GenericSerializeReference(mode: GenerateMode.AssemblyCSharp)]
    // public IMyInterface<int> Value { get; set; }

    // 1. create a field named _Value with `IBase` type
    //    which should be able to serialized by `SerializeReference` attribute
    [SerializeReference, GenericSerializeReferenceGeneratedField]
    private GenericSerializeReference.IBase _Value;
    
    // 2. inject code into property's getter and setter
    //    make sure property get value from serialized field first
    //    and setter set serialized field into null to avoid get from it next time.
    [GenericSerializeReference]
    public IMyInterface<int> Value
    {
        get
        {
            return (IMyInterface<int>) _Value ?? <Value>k__backingField;
        }
        set
        {
            <Value>k__backingField = value;
            _Value = null;
        }
    }
}

// GenericSerializeReference.OverrideAssemblyCSharp or AssemblyCSharp.dll
// 3. gather derived types of property (`IMyInterface<int>`)
//    then generate a non-generic version of those types and make them all implement `IBase` interface
namespace <GenericSerializeReference>
{
    static class IMyInterface`1<System_Int32>
    {
        class MyIntObject : global::MyIntObject, GenericSerializeReference.IBase {}
    }
}

Embed Mode

Embed generated derived classes beside the property with GenericSerializeReferenceAttribute.

// Embed into current class
public class MyMonoBehavior : MonoBehaviour
{
    // [GenericSerializeReference(mode: GenerateMode.Embed)]
    // public IMyInterface<int> Value { get; set; }

    // 1. create a field named _Value with `IBase` type
    //    which should be able to serialized by `SerializeReference` attribute
    [SerializeReference, GenericSerializeReferenceGeneratedField]
    private <Value>__generic_serialize_reference.IBase _Value;
    
    // 2. inject code into property's getter and setter
    //    make sure property get value from serialized field first
    //    and setter set serialized field into null to avoid get from it next time.
    [GenericSerializeReference]
    public IMyInterface<int> Value
    {
        get
        {
            return (IMyInterface<int>) _Value ?? <Value>k__backingField;
        }
        set
        {
            <Value>k__backingField = value;
            _Value = null;
        }
    }
    
    // 3. gather derived types of property (`IMyInterface<int>`)
    //    then generate a non-generic version of those types and make them all implement `IBase` interface
    private static class <Value>__generic_serialize_reference
    {
        public interface IBase {}
        public class MyIntObject : global::MyIntObject, IBase {}
    }

}
  • AssemblyCSharp mode: generate any type from AutoReferenced assemblies.
  • AssemblyCSharp mode with custom GenericSerializeReference.OverrideAssemblyCSharp assembly: able to control which type should be generated by choosing certain referenced assemblies.
  • Embed mode: generate any type from assemblies referenced by processed assembly.

License

MIT

Drawer modified from TextusGames's UnitySerializedReferenceUI with MIT license

About

[GenericSerializeReference] attribute to serialize generic property in Unity3D

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages