Serialized interface fields

Problem

It is not possible to assign an interface-type field in the inspector. The field does not show up.

public interface ICharacterData { ... }

[CreateAssetMenu]
public class CharacterData : ScriptableObject, ICharacterData { ... }

public class Demo : MonoBehaviour
{
  [SerializeField] private CharacterData data1; // works
  [SerializeField] private ICharacterData data2; // does not work
}

Why are interface-type fields not serializable?

Unity’s serialization system needs to know the type of the serialized field, because it will do something different depending on the type. Basic types, structs, enums, built-in types, and serializable types gets serialized by value, similar to how JsonUtility would serialize them. Anything derived from UnityEngine.Object (including ScriptableObject, MonoBehaviour, and any asset in the project) gets serialized by reference instead, i.e. like this:

data1: {fileID: 11400000, guid: 961cd3964a6354efb9ad01cbfae84d63, type: 2} In order for the serialization system to determine whether to serialize a field by value or by reference it needs to know whether the type is derived from UnityEngine.Object or not.

In our example, ICharacterData could be anything. We could have a serializable class implementing the interface. We could also have a ScriptableObject asset implementing it. The serialization system can not know which case we’re dealing with and the inspector can not know whether to show an object field or a value field.

But interface-type fields are serializable!

Since Unity 2019.3 interface-type fields are serializable. Instead of the SerializeField attribute, we have to use the SerializeReference attribute.

Problem solved, right? …

public class Demo : MonoBehaviour
{
  [SerializeField] private CharacterData data1; // works
  [SerializeReference] private ICharacterData data2; // still does not work
}

Contrary to what the name of the attribute implies, SerializeReference serializes the field by value, not by reference.
It does solve an important problem related to serializing polymorphic types by value, but it does not solve our problem.

Just use ScriptableObject!

The documentation suggests that we should use ScriptableObject here. That’s a great suggestion. In fact, we are using ScriptableObject here. We just want to use an interface as the field type for it.
Why though? Why are we being so stubborn? Why not use the concrete type instead?

What is so special about interfaces?

Remember the diamond problem? It leads to tricky ambiguities. That’s why C# only allows single inheritance. In C# a class can not inherit from multiple base classes at the same time. It can, however, implement multiple interfaces at the same time. That’s because interfaces don’t have the diamond problem. Interfaces have superpowers.

Programming in C# means programming with interfaces. If you don’t do that then you’re seriously limiting the kinds of inheritance patterns that can be used. Chances are you won’t notice the problem until the codebase is very large, because concrete types and abstract base classes go a long way. Until they don’t. At which point the only way forward is to build a parallel hierarchy of interfaces and replacing all uses of concrete types with their respective interface type.

Except doing so breaks serialized fields.

I can give you multiple examples of real world codebases of real games where using interfaces became a necessity, but those examples are all big, very specific, and hard to understand without context. For now, please just take my word for it: interfaces are important. Being able to refer to a type by interface is important. Other programmers reading this might want to confirm.

What can we do about it?

public class Demo : MonoBehaviour
{
  [SerializeField, Restrict(typeof(ICharacterData))]
  private UnityEngine.Object _data; // works

  public ICharacterData data => _data as ICharacterData;
}

For our field we want Unity to show an object field in the inspector. So we might as well use UnityEngine.Object as the type. However, we only want to be able to assign objects that implement our interface. We can restrict what can be assigned to the object field by implementing a custom attribute and property drawer.

That essentially solves our problem. It’s a bit clunky and the extra get-only property with cast smells bad, but it works. Without help from Unity, this is as good as it gets.

Feature request

public class Demo : MonoBehaviour
{
  [SerializeField] private ICharacterData data; // this should just work
}

Unity can serialize references to assets. In fact, Unity can serialize references to assets via base type. Assigning a CharacterData asset to a field of type UnityEngine.Object via the inspector works just fine. Unity serializes the reference to the asset and on deserialization Unity looks up the concrete asset by reference (fileId, guid, type) and assigns it to the field.

I don’t see any reason why it would be impossible to do the same for interface-type fields.

It wouldn’t even require a new attribute. Just use SerializeField. The semantics are unambiguous:

  • If the type is an interface or inherited from UnityEngine.Object, serialize by reference.
  • In all other cases, serialize by value.

It wouldn’t even clash with SerializeReference. Users who want to have their interface-typed field serialized by value can still do so.

TL;DR: Please support serializing interface-type fields the same as UnityEngine.Object type fields.

52 Likes

While this doesn’t address the feature request (I’m the wrong person for that), you could do this:

class CharacterData : ICharacterData
{
}

class ICharacterData : ScriptableObject, ICharacterDataBase
{
}

interface ICharacterDataBase
{
     // Actual interface methods
}

public class Demo : MonoBehaviour
{
    [SerializeField] private ICharacterData data; // this works now.
}
3 Likes

I think the point is to be able to reference any UnityEngine.Object subclass instance (not just ScriptableObject) with an interface.
PLUS in your code, ICharacterData is not even an interface but a class. And you can only inherit from “one” class.
It needs to be an interface so another class can implement it as one of the many interfaces it wants to implement.

2 Likes
  • Hence the alternative solution you provided wouldn’t solve what OP’s proposed feature is trying to solve/can provide.

Yes, that would “solve” the example I used to illustrate the problem. However, as pointed out, this solution is not an option, because ICharacterData is not an interface.

Here is an equally contrived example that is less easy to “solve” using existing functionality:

public interface IValueProvider
{
  float value { get; }
}

[CreateAssetMenu]
public class FixedValueProvider : ScriptableObject, IValueProvider
{
  [SerializeField] private float _value;

  public float value => _value;
}

public class DynamicValueProvider : MonoBehaviour, IValueProvider
{
  private float _value;

  public float value => _value;

  void Update()
  {
    _value += Time.deltaTime;
  }
}

public class ValueUI : MonoBehaviour
{
  [SerializeField] private IValueProvider _valueProvider; // does not work!
  [SerializeField] private Text _text;

  void Update()
  {
    _text.text = string.Format("Value: {0}", _valueProvider.value);
  }
}

As you can see, we have two classes derived from different base classes, and we want to be able to assign an instance of either one of them to a field. The only way to unify them is through an interface. This is why interfaces are important.

7 Likes

My input, rather than waiting for Unity to implement this (if ever), just invest in Odin Inspector which can serialise interfaces, including that of interfaces implemented by UnityEngine.Objects.

2 Likes

Odin serializer doesnt work with nested prefabs

5 Likes

Thank you for pointing out things I already know.

Sorry, this was for everyone, as your previous post

5 Likes

Yeah I was being a bit of an ass.

In any case Odin serialisation is currently deprecated for prefabs in general, BUT, the Odin devs are actively working with Unity’s prefab team to bring full support back.

None of this is on topic, mind you.

1 Like

+1000 on this feature request. My use case is dependency injection. It’s way easier to test a component when its dependencies (public fields that reference other components) are declared as interfaces rather than concrete types. Then I can simply mock those interfaces in tests and not have to build out an entire scene hierarchy. Without interfaces being serialized, I have had to build my own custom dependency injection library from scratch. While I understand that this is all way easier said than done, for all of the aforementioned reasons, this is really something that Unity should support natively.

14 Likes

I don’t really understand why we are still here asking for that feature. This is one of the main missing features for people that wants to create good project architectures.

C’mon Unity team, that should not be a big effort for you to implement, or am I missing something?

2 Likes

It would require a completely different method of serialisation to accomplish, so definitely not a small task. Considering how completely divergent SerializeReference is to SerializeField, I would I imagine to serialisation required to allow either referenced types or managed Unity objects to be serialised by the same field is it’s own barrel of fish.

FYI the Odin serialiser is open source and completely free to use: https://github.com/TeamSirenix/odin-serializer

No inspector support, but it shouldn’t be hard to get your own field working.

1 Like

If you don’t want to use Odin for some reason, I’ve created small drawer that allows to use interfaces with [SerializeReference] that have serializable-s as implementations(not UnityEngine.Object). It will generate drop down in UI, so you can select concrete implementation in inspector.
https://github.com/ArseniiRudenko/unity_serialize_interface

2 Likes

I see, thanks for the explanation. But it should definitely have a way for Unity to do it as a built-in feature with no need of that big change since we can find some repos where people create workarounds such as Odin and @IAmTheQueenOfEngland , for instance.

in 2021 I tried using the one from Odin, but I did not like it since the serialized data does not work well when you need to rename the class or namespace of the object added to the inspector. So I gave up and changed it to serialize Object or ScriptableObject and then having a property to cast it to my interface like:

[SerializeField]
private ScriptableObject[] controllers;

private IEnumerable<IController> Controllers => controllers.OfType<IController>();
1 Like

+1000 on this feature request. Composition over inheritance is almost always better eh

4 Likes

I would also like to see this feature implemented.

3 Likes

+1

2 Likes

+1
Everyone who loves clean architecture knows the importance of interfaces

4 Likes

Hi, for me it worked like this:

using UnityEngine;

public class Demo : MonoBehaviour
{
    public Object characterDataObject;
    private ICharacterData characterData;

    private void Start()
    {
        characterData = characterDataObject as ICharacterData;
    }
}

With the “Object” type, the scriptable object can be attached. Then just do a casting for the interface.

Hope it’s useful!

2 Likes