0
\$\begingroup\$

There is plenty of popular opinion in the world surrounding the language and when developers should use a field or a property. The general consensus is that fields should remain private to a class, and, it's what I've been doing for more than a decade now.

With that, a lot of younger developers, with much less experience in the language, may not even know that fields and properties are related since modern really has no need for backing fields in 60% or more of use cases. Instead we have the auto-properties that juniors are used to seeing:

public int MyInteger { get; set; }

Most of them do not yet have the knowledge that this is actually syntactical sugar, and that the backing fields are automatically created at compile time, effectively reducing the amount of code you have to write (cough cough, copy and paste):

private int _myInteger;
public int MyInteger {
    get { return _myInteger; }
    set { _myInteger = value; }
}

Even seeing expression bodied methods in the getter and setter is an infrequent occurrence in modern code. With that, after the many, many years that Unity has been on the market, we still don't have natural support for using properties in the inspector for our scripts. Excluding the following work arounds, which both use field serialization:

// Pre C# 7.3 support:
[SerializeField] private int _myInteger;
public int MyInteger { get => _myInteger; set => _myInteger = value; }

// Post C# 7.3 support:
[field: SerializeField] public int MyInteger { get; set; }

Both work arounds utilize field serialization to get the exposure in the inspector for our scripts.


After all these years, what is the reason that Unity does not support using properties in the inspector instead of fields? I suspect it has something to do with one, if not more of the following:

  • Existing asset support.
  • Language support barriers (perhaps fields are easier to support?).
  • Effort/Payoff (perhaps too much effort, for too little payoff?).
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Maybe because those properties are internalliy compiled as Getter/Setter methods (stackoverflow.com/questions/23102639/…) Unity hasn't made any support for them. The only point (or one of the main points) of having exposed fields in the inspector is so the user can inject it with dependencies or data, hence for serializing data only. What would happen if inside the get{}/set{} bodies the user adds more functionality that has, for example, run-time dependencies? that is just one example that it comes to my mind. \$\endgroup\$ Jul 12 at 0:10
  • \$\begingroup\$ "Both work arounds utilize field serialization to get the exposure in the inspector for our scripts." The Unity inspector is serialization-based. It literally walks through the serialized fields of the object one by one. So, this is as "native" a level of support as we'd expect to see here. A variable that is not serialized would have no place for the inspector to store its value to be saved in the asset or scene file. So I'm not sure what kind of non-serialization solution you have in mind. \$\endgroup\$
    – DMGregory
    Jul 12 at 1:59
2
\$\begingroup\$
  1. As LifGwaethrakindo noted, properties can have side effects. Imagine we had this property on a character:
[SerializeProperty]
public int HP {
    get => hp;
    set {
        hp = value;
        if (value == 0) GameObject.Destroy(this);
    }
}

The setter logic makes sense during gameplay, but not in the Editor. The default value for HP would be 0. If we forget to assign a new value in the inspector, the HP will stay at 0. The next time the scene/prefab is deserialized in the Editor, the character will be destroyed immediately (and we'll get a warning about how we should use DestroyImmediate() when destroying objects in the Editor from code while the game isn't running).

Such a problem could be avoided like this:

[SerializeProperty]
public int HP {
    get => hp;
    set {
        hp = value;
        if (Application.isPlaying && value == 0) GameObject.Destroy(this);
    }
}

but it would be very easy to forget the Application.isPlaying condition and encounter totally bizarre behavior. Such issues would look like Editor bugs and would cause the Unity QA team to be inundated with bug reports.

The engine could theoretically bypass the setter logic and only set the value of the underlying variable, but then what would happen in a case like this?

private int hp;
private int maxHP;

[SerializeProperty]
public int MaxHP {
    get => maxHP;
    set {
        maxHP = value;
        hp = value;
    }
}

When we deserialized, hp would not get set and would remain at 0.

  1. If we could serialize both fields and properties, there would be the potential for situations like this:
[SerializeField] private int someValue = 50;

[SerializeProperty]
public int SomeValue {
    get => someValue;
    set => someValue = value * 2;
}

What happens when we deserialize this? If the property is deserialized first, the value won't change, but if the field is deserialized first, the value will double each time.

This can be resolved by only allowing serialization of properties, and not of fields, but that would be a massive breaking change that would destroy compatibility with every game made with any prior version of Unity. Imagine upgrading a large game to the newest version of Unity and then having to go through every single serialized field in every component script and replace them with serialized properties that don't have unexpected side effects.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.