3
\$\begingroup\$

Using the following code, only one variable is shown in the inspector:

[SerializeField] private GameObject _obj;

This makes sense, since there is only one field.


However, with this code, there is still only one variable in the inspector. I would expect there to be two variables:

public GameObject Obj => _obj;
[SerializeField] private GameObject _obj;

enter image description here


Why aren't there two variables in the second example?

\$\endgroup\$
1
  • 1
    \$\begingroup\$ I made some tweaks to your question for clarity. If I misinterpreted, please let me know and I'll revert. \$\endgroup\$
    – Evorlor
    Nov 26 '21 at 0:30
11
\$\begingroup\$
public GameObject Obj => _obj;

This is not a variable, it's a property. It's equivalent to this:

public GameObject Obj {
    get {
        return _obj;
    }
}

So it's just syntactic sugar for a public GameObject GetObject() { return _obj; } method.

Since Unity's Inspector displays serialized fields, not methods, there's nothing here for it to display (no stored value that belongs to the symbol Obj). The stored value belongs to the "backing field" _obj, so that field is the only one the Inspector shows.

You can ask the compiler to automatically generate a private backing field for you instead of writing one yourself, using an auto-implemented property:

public GameObject Obj { get; private set; }

This is equivalent to....

private GameObject _hiddenCompilerGeneratedField;
public GameObject Obj {
    get {
        return  _hiddenCompilerGeneratedField;
    }
    private set {
         _hiddenCompilerGeneratedField = value;
    }
}

This way your code can still set the private backing field by calling the setter (like Obj = foo;), even though we don't have a visible name to call that field, since the compiler is generating it for us.

That's a convenient way to make a variable that only code in this class can set, while other code can still read it - useful for maintaining invariants and minimizing the surface area for bugs. If the variable ever gets set to the wrong value, there are fewer places that could have happened, making the problem easier to reason about.

The trouble with this is that the compiler generates that field as private, without the [SerializeField] attribute. Since Unity's Inspector is built on its serialization system, which only serializes fields that are public or marked with [SerializeField], this compiler-generated field gets skipped over.

So in the example code you've found with both Obj and _obj, the author is trying to work around this limitation by declaring their backing field and public getter manually.

There's a somewhat neater way to do that though, if all you want is a serialized private field with a public getter. You can use a modified version of the [SerializeField] attribute to target the hidden backing field generated by the compiler when declaring an auto-property:

[field: SerializeField]
public GameObject Obj { get; private set; }

Just like the Obj and _obj example, this also creates a serializable field that can show in the inspector and be set by code inside this class, and exposes it via a public property getter. The only difference here is that now we can set the private field via Obj's setter, without needing to define a separate name for it ourselves.

Just remember that when exposing the backing field of a property to the inspector like this, you're exposing only the storage for the variable - not the get and set methods. As the Unity documentation notes:

If you use properties in your script, any of the property getters and setters are never called when you view or change values in the Inspector windows as Unity serializes the Inspector window fields directly. This means that: While the values of a field in the Inspector window represent script properties, changes to values in the Inspector window do not call any property getters and setters in your script.

That's not an issue for auto-properties, since by definition they have nothing else that's supposed to happen in their getters/setters, but it can be a risk if you define your serialized backing field separately and then start putting extra code into the property getter/setter, forgetting that the Inspector and deserializer will bypass those entirely.

\$\endgroup\$
2
  • \$\begingroup\$ Wow I didn’t know you could expose a property. Do you need the “field:” part for it to work? \$\endgroup\$
    – Adam B
    Nov 26 '21 at 1:52
  • 4
    \$\begingroup\$ That seems like a question you could answer for yourself by testing it. You may also consider that I probably didn't bother typing extra characters just for the heck of it. 😉 \$\endgroup\$
    – DMGregory
    Nov 26 '21 at 2:20

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .