Skip to main content

SerializedProperty Extensions

Write editor properties in one line — and reach the actual C# field the property shows in the Inspector.

Quick start​

Before — Unity APIAfter — FastTools
var manaCost = serializedObject
.FindProperty("_manaCost");

serializedObject.Update();
manaCost.intValue = 42;
serializedObject
.ApplyModifiedProperties();
var manaCost = serializedObject
.FindProperty("_manaCost");

manaCost
.Update()
.SetIntAndApply(42);

Writing values​

Before — Unity APIAfter — FastTools
manaCost.intValue = 42;
manaCost.SetInt(42);
manaCost.intValue = 42;
manaCost.serializedObject
.ApplyModifiedProperties();
manaCost.SetIntAndApply(42);
manaCost.intValue = 42;
manaCost.serializedObject
.ApplyModifiedPropertiesWithoutUndo();
manaCost
.SetIntAndApplyWithoutUndo(42);

Supported types​

ValuesSetters
NumbersSetInt, SetUint, SetLong, SetUlong, SetFloat, SetDouble
Text, bool and hashSetString, SetBool, SetHash128
VectorsSetVector2, SetVector2Int, SetVector3, SetVector3Int, SetVector4, SetQuaternion
AreasSetRect, SetRectInt, SetBounds, SetBoundsInt
Unity typesSetColor, SetGradient, SetAnimationCurve
Object IDsSetEntityId

Each has a SetValue overload: SetValue(42) calls SetInt, so the argument type must match the field type.

Enums, arrays and references​

UnityFastTools
enumValueIndex = 1SetEnumIndex(1)
enumValueFlag = flagsSetEnumFlag(flags)
arraySize = 5SetArraySize(5)
arraySize += nAddArraySize(n = 1)
arraySize -= nRemoveArraySize(n = 1)
managedReferenceValue = valueSetManagedReference(value)
objectReferenceValue = valueSetObjectReference(value)
boxedValue = valueSetBoxed(value)
exposedReferenceValue = valueSetExposedReference(value)

Update() and Apply…()​

UnityFastTools
serializedObject.Update()Update()
serializedObject.UpdateIfRequiredOrScript()UpdateIfRequiredOrScript()
serializedObject.ApplyModifiedProperties()ApplyModifiedProperties()
serializedObject.ApplyModifiedPropertiesWithoutUndo()ApplyModifiedPropertiesWithoutUndo()

They are called on the property and, like every setter, return it.

The C# field from the Inspector​

Three methods find the C# field the property shows in the Inspector: its type, the field itself and the object that holds it.

public class AbilityBook : MonoBehaviour
{
[SerializeField]
private List<Ability> _abilities = new() { new Ability() };

[SerializeReference]
private IAbilityEffect _effect = new BurnEffect();
}

[Serializable]
public class Ability { public string Name = "Fireball"; }

[Serializable]
public class BurnEffect : IAbilityEffect { public float Damage = 5f; }
Property pathGetPropertyType()GetFieldInfo()GetDeclaringInstance()
_abilitiesList<Ability>AbilityBook._abilitiesthe AbilityBook
_abilities[0]AbilityAbilityBook._abilitiesthe AbilityBook
_abilities[0].NamestringAbility.Namethe Ability at index 0
_effectIAbilityEffectAbilityBook._effectthe AbilityBook
_effect.DamagefloatBurnEffect.Damagethe BurnEffect instance
  • if the field is not found, GetPropertyType() and GetFieldInfo() return null, and GetDeclaringInstance() does only when the owning object is not found;
  • the methods read only the first target object and only applied values.
warning

When the field's owner is a struct, GetDeclaringInstance() returns a boxed copy. Changes to that copy never reach the original; write values through the SerializedProperty instead.

Member name and drawing checks​

Property pathGetMemberName()IsArrayElement()HasFoldout()
_abilities[0]"_abilities"truetrue
_abilities[0].Name"Name"falsefalse
_effect"_effect"falsefalse
note

HasFoldout() returns true for a plain compound field with visible children, so it can differ from the Inspector: a [SerializeReference] field gets a foldout in the Inspector but false here, and custom PropertyDrawers are not taken into account.

Persistent()​

Persistent() returns a copy of the property on its own SerializedObject, so it can be used later, for example in EditorApplication.delayCall.

Before — Unity APIAfter — FastTools
var source = manaCost.serializedObject;
var independentObject = new SerializedObject(
source.targetObjects, source.context);
var independent = independentObject
.FindProperty(manaCost.propertyPath);
var independent = manaCost.Persistent();
  • the copy belongs to the caller: dispose it together with its serializedObject;
  • unapplied writes of the source property do not reach the copy;
  • if the property is missing on the targets, the method returns null.
var independent = manaCost.Persistent();
if (independent == null) return;

EditorApplication.delayCall += () =>
{
using (independent.serializedObject)
using (independent)
independent.Update().SetIntAndApply(42);
};

Package sample​

In EditorTools, the Halve cooldown, +5 MP button writes two properties with SetFloat and SetIntAndApply.