SerializedProperty.NextVisible doesn't work with HideInInspector

When I iterate over an Object’s variables with the following code, it excludes fields marked with HideInInspector:

SerializedProperty property = serializedObject.GetIterator();
if( property.NextVisible( true ) )
{
    do
    {
        // Process SerializedProperty
    } while( property.NextVisible( false ) );
}

When I use property.Next instead, it works. BUT, this results in iterating over a bunch of extra properties like “m_CorrespondingSourceObject”, “m_Father” and “m_PrefabAsset” (these are Unity’s own native properties).

Is there a way to iterate over the SerializedPropery objects returned by property.NextVisible AND the SerializedProperty objects corresponding to [HideInInspector] fields but NOT the SerializedProperty objects for “m_Father” like native properties?

I suppose it comes down to finding ways to answer the three questions going into your decision for showing or not.

I imagine it may be possible with reflection, but I cannot state that for certain as I have not tried personally.

In the end, I had to use reflection as you’ve suggested and it worked. In my benchmarks, it was up to ~10 times slower but it should be fine as long as this function isn’t called thousands of times per second:

delegate FieldInfo FieldInfoGetter( SerializedProperty p, out Type t );

public static void IterateProperties( SerializedObject serializedObject )
{
#if UNITY_2019_3_OR_NEWER
    MethodInfo fieldInfoGetterMethod = typeof( Editor ).Assembly.GetType( "UnityEditor.ScriptAttributeUtility" ).GetMethod( "GetFieldInfoAndStaticTypeFromProperty", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static );
#else
    MethodInfo fieldInfoGetterMethod = typeof( Editor ).Assembly.GetType( "UnityEditor.ScriptAttributeUtility" ).GetMethod( "GetFieldInfoFromProperty", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static );
#endif
    FieldInfoGetter fieldInfoGetter = (FieldInfoGetter) Delegate.CreateDelegate( typeof( FieldInfoGetter ), fieldInfoGetterMethod );

    SerializedProperty propertyAll = serializedObject.GetIterator();
    SerializedProperty propertyVisible = serializedObject.GetIterator();
    if( propertyAll.Next( true ) )
    {
        bool iteratingVisible = propertyVisible.NextVisible( true );
        do
        {
            bool isVisible = iteratingVisible && SerializedProperty.EqualContents( propertyAll, propertyVisible );
            if( isVisible )
                iteratingVisible = propertyVisible.NextVisible( false ); // Change it to true if you want to enter child properties, as well
            else
            {
                Type propFieldType;
               
                // Internal Unity variables don't seem to have a FieldInfo but when SerializedProperty.type is "Array", we must consider it
                // visible to avoid false negatives because even though "Array" type doesn't have a FieldInfo, it can be a visible array property
                isVisible = propertyAll.type == "Array" || fieldInfoGetter( propertyAll, out propFieldType ) != null;
            }

            if( isVisible )
                Debug.Log( propertyAll.name );
        } while( propertyAll.Next( false ) ); // Change it to true if you want to enter child properties, as well
    }
}

But I’ll be calling this function thousands of times in a couple of my plugins, so I’ll be caching the HideInInspector properties for maximum performance.

2 Likes

My hats off to you! Well done. I think I would have been lazy and just lived with all the extra fields and ignored them. Good on you for making it the way you want it, and thanks for posting the code for all of us to enjoy.

1 Like

That works too

string[] excludes = { "m_Script" };

SerializedProperty Iterator = this.serializedObject.GetIterator();

Iterator.NextVisible(true);

while (Iterator.Next(false))
{
if (excludes.Contains(Iterator.name)) continue; EditorGUILayout.PropertyField(Iterator, true);
}

Prefab objects’ iterator yields a bunch of other stuff that must be included in “excludes”, as well. If I remember correctly, one of them was named something like m_PrefabAsset. This can be a legit variable name in users’ codebases so I wouldn’t rely on variable name checking for this task.