C# 7.3 [field: SerializeField] support

C# 7.3 introduces the ability to target backing fields of properties with attributes. This allows us to do some very nice things, like this:

public class TestScript : MonoBehaviour {

    [field: SerializeField]
    public int foo { get; private set; }

This works, but it's not exactly an ideal look:

3820039--321547--upload_2018-10-25_15-23-50.png

Is there any plans to give this proper support?

Something that would have been even more powerful, and which doesn't exist at all now, is to have read-only properties with serialized data:

public class TestScript : MonoBehaviour {

    [field: SerializeField]
    public int bar { get; }
}

I'm not quite sure how the internals of this works, but Unity doesn't seem to try to serialize the backing field. This syntax would allows us to encode that something should only be settable from the inspector, which isn't something we can do currently.

This was brought up on the scripting forums , I figured I'd make a post here as it's more likely to reach the relevant people.

20 Likes

It looks like it could make sense to properly support this.

We would need to investigate exactly what the C# compiler emits for this new feature and see how well it fits with the Unity serialization system to know whether it is feasible to add support for without major changes.

I've added it to our backlog to look into this and also linked this thread on the task.

19 Likes

Let's hope it makes its way onto the list of planned features! I've been waiting a long while for auto properties to be serializable (without being dependent on the Odin Serializer asset)

1 Like
[SerializeField] int _intField;
public int IntProperty => _intField;

I find this kind of boilerplate very often in runtime readonly data fields. It'd be great to have this feature to reduce the amount of noise when working with said code.

4 Likes

I second this! This will make good C# code using properties so much easier.

Currently you can play around with a PropertyDrawer to rename a field, but unfortunately I can't find a way to make this work with arrays (the PropertyDrawer only seems to receive the elements of the array, instead of the array itself, so it can't be renamed).

class RenameFieldAttribute : PropertyAttribute
{
    public string Name { get; }

    public RenameFieldAttribute(string name) => Name = name;

#if UNITY_EDITOR
    [CustomPropertyDrawer(typeof(RenameFieldAttribute))]
    class FieldNameDrawer : PropertyDrawer
    {
        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            string[] path = property.propertyPath.Split('.');
            bool isArray  = path.Length > 1 && path[1] == "Array";

            if (!isArray && attribute is RenameFieldAttribute fieldName)
                    label.text = fieldName.Name;

            EditorGUI.PropertyField(position, property, label, true);
        }
    }
#endif
}

Example:

class TestBehaviour : MonoBehaviour
{
    [field: SerializeField]
    [field: RenameField(nameof(MyProperty))]
    int MyProperty { get; set; } = 10;

    void Start()
    {
        Debug.Log(MyProperty);
    }
}

3944824--337543--upload_2018-11-30_10-55-36.png

1 Like

[quote=“TheZombieKiller”, post:6, topic: 719343]
Currently you can play around with a PropertyDrawer to rename a field, but unfortunately I can’t find a way to make this work with arrays (the PropertyDrawer only seems to receive the elements of the array, instead of the array itself, so it can’t be renamed).

class RenameFieldAttribute : PropertyAttribute
{
    public string Name { get; }

    public RenameFieldAttribute(string name) => Name = name;

#if UNITY_EDITOR
    [CustomPropertyDrawer(typeof(RenameFieldAttribute))]
    class FieldNameDrawer : PropertyDrawer
    {
        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            string[] path = property.propertyPath.Split('.');
            bool isArray  = path.Length > 1 && path[1] == "Array";

            if (!isArray && attribute is RenameFieldAttribute fieldName)
                    label.text = fieldName.Name;

            EditorGUI.PropertyField(position, property, label, true);
        }
    }
#endif
}

Example:

class TestBehaviour : MonoBehaviour
{
    [field: SerializeField]
    [field: RenameField(nameof(MyProperty))]
    int MyProperty { get; set; } = 10;

    void Start()
    {
        Debug.Log(MyProperty);
    }
}

[/quote]
Is it possible to make this work automatically with [field: SerializeField]?

Tangentially related: If the decision is to add in support for this, and it works on readonly properties, can we expand this to fields marked readonly as well? This should make it easier to declaratively state that the value is meant to be serialized, but explicitly disallowed to be modified at runtime.

@lukaszunity Any update?

2 Likes

I know this is currently only used for enum properties, but allowing to override the display name (without changing the (backing) field's name) via UnityEngine.InspectorNameAttribute would be great and solve this as well as potentially lots of other use cases. (This attribute could have just been EnumMemberAttribute instead btw...)

Better handling by renaming backing fields automatically would still be preferred by me, though.

This is still on our backlog and is currently not a high priority task.

Oh man, add my vote to the pile... I've had my fingers crossed for this for quite literally years.

1 Like

Just stumbled upon this when Rider suggested the new [field: ] syntax. But renaming the label in the Inspector is not really a solution, because when you look at the serialized asset, it uses the compiler-generated backing field name for property name as well, which is not a good idea to use like that (this is compiler-specific, backing field naming can change any time).

IMO properly supporting auto-properties is the final piece of the puzzle in bringing Unity C# on the same level as "normal" C# development. Unity's serializer should understand auto-properties as it's own thing and serialize them using human-written property name along with the correct Label.

2 Likes

I just want to add a little bump on this. I write so much boiler plate private backing field code... this would literally save me hundreds of lines of typing a week.

Please please please please add this feature.

2 Likes

I honestly want this MORE than any other possible Unity feature right now.

4 Likes

How Unity Dev. team approach a task priority assignment ? There might be ton of "low priority" tasks that might have high impact/effort ratio because of low effort and simply never get solved because of low impact the priority is set low. Maybe it should be set based on the impact/effort ratio rather than just by impact itself. (It's just an random idea, I have no idea how this actually works inside Unity or if this is a good idea at all or if this problem is actually low effort or not)

1 Like

For anyone looking for a (kinda intrusive) solution: Malimbe is a project that allows code weaving in Unity and supports "serialized properties". It ensures the backing field the compiler generates is renamed to match the property's name, thus works around this issue.
https://github.com/ExtendRealityLtd/Malimbe/#propertyserializationattributefody

1 Like

Oooooh, @bddckr I'm going to give this a try tonight.

This would be extremely valuable and would allow us to remove a large amount of unavoidable boilerplate. It would also allow us to further eliminate some of our Unity-specific style guidelines.

Detecting auto-generated backing fields should be trivial. Roslyn always attaches a System.Runtime.CompilerServices.CompilerGeneratedAttribute to them, and they have a distinctive name which is not expressible using any .NET language that I am aware of (e.g. <ExampleVariable>k__BackingField).

2 Likes

[quote=“lukaszunity”, post:11, topic: 719343]
This is still on our backlog and is currently not a high priority task.
[/quote]

It should be! Between, like, everything planned for 2019.2 and this, I’d rather have this! It would effectively make every single thing I do a bit more convenient.

9 Likes