private static string[] CreateNotes(MemberInfo member, SerializationFlags flags, SerializationBackendFlags serializationBackend, string serializationPolicyId)
        {
            List <string> notes  = new List <string>();
            StringBuilder buffer = new StringBuilder();

            // Handle the default serialization policy.
            if (serializationBackend.HasNone(SerializationBackendFlags.Odin) || flags.HasAll(SerializationFlags.DefaultSerializationPolicy))
            {
                // Member type
                if (flags.HasAll(SerializationFlags.Property | SerializationFlags.AutoProperty))
                {
                    buffer.AppendFormat("The auto property {0} ", member.GetNiceName());
                }
                else if (flags.HasAll(SerializationFlags.Property))
                {
                    buffer.AppendFormat("The non-auto property {0} ", member.GetNiceName());
                }
                else
                {
                    if (flags.HasAll(SerializationFlags.Public))
                    {
                        buffer.AppendFormat("The public field {0} ", member.GetNiceName());
                    }
                    else
                    {
                        buffer.AppendFormat("The field {0} ", member.GetNiceName());
                    }
                }

                // Is the member serialized?
                if (flags.HasAny(SerializationFlags.SerializedByOdin | SerializationFlags.SerializedByUnity))
                {
                    buffer.Append("is serialized by ");

                    // Who?
                    if (flags.HasAll(SerializationFlags.SerializedByUnity | SerializationFlags.SerializedByOdin))
                    {
                        buffer.Append("both Unity and Odin ");
                    }
                    else if (flags.HasAll(SerializationFlags.SerializedByUnity))
                    {
                        buffer.Append("Unity ");
                    }
                    else
                    {
                        buffer.Append("Odin ");
                    }

                    buffer.Append("because ");

                    // Why?
                    var relevant = flags & (SerializationFlags.Public | SerializationFlags.SerializeFieldAttribute | SerializationFlags.NonSerializedAttribute);

                    if (flags.HasAll(SerializationFlags.OdinSerializeAttribute) && serializationBackend.HasAll(SerializationBackendFlags.Odin)) // The OdinSerialize attribute is only relevant when the Odin serialization backend is available.
                    {
                        relevant |= SerializationFlags.OdinSerializeAttribute;
                    }

                    switch (relevant)
                    {
                    case SerializationFlags.Public:
                        buffer.Append("its access modifier is public. ");
                        break;

                    case SerializationFlags.SerializeFieldAttribute:
                        buffer.Append("it the [SerializeField] attribute is defined. ");
                        break;

                    case SerializationFlags.Public | SerializationFlags.SerializeFieldAttribute:
                        buffer.Append("it the [SerializeField] attribute is defined, and it's public. ");
                        break;

                    case SerializationFlags.OdinSerializeAttribute:
                        buffer.Append("it the [OdinSerialize] attribute is defined. ");
                        break;

                    case SerializationFlags.Public | SerializationFlags.OdinSerializeAttribute:
                        buffer.Append("it the [OdinSerialize] attribute is defined, and it's public.");
                        break;

                    case SerializationFlags.SerializeFieldAttribute | SerializationFlags.OdinSerializeAttribute:
                        buffer.Append("it the [SerializeField] and [OdinSerialize] attributes are defined. ");
                        break;

                    case SerializationFlags.Public | SerializationFlags.SerializeFieldAttribute | SerializationFlags.OdinSerializeAttribute:
                        buffer.Append("its access modifier is public and the [SerializeField] and [OdinSerialize] attribute are defined. ");
                        break;

                    case SerializationFlags.OdinSerializeAttribute | SerializationFlags.NonSerializedAttribute:
                    case SerializationFlags.Public | SerializationFlags.OdinSerializeAttribute | SerializationFlags.NonSerializedAttribute:
                    case SerializationFlags.SerializeFieldAttribute | SerializationFlags.OdinSerializeAttribute | SerializationFlags.NonSerializedAttribute:
                    case SerializationFlags.Public | SerializationFlags.SerializeFieldAttribute | SerializationFlags.OdinSerializeAttribute | SerializationFlags.NonSerializedAttribute:
                        buffer.Append("the [OdinSerialize] and [NonSerialized] attribute are defined. ");
                        break;

                    default:
                        buffer.Append("(MISSING CASE: " + relevant.ToString() + ")");
                        break;
                    }

                    // Empty the buffer.
                    if (buffer.Length > 0)
                    {
                        notes.Add(buffer.ToString());
                        buffer.Length = 0;
                    }

                    // Why is the value not serialized by Unity?
                    if (serializationBackend.HasAll(SerializationBackendFlags.Unity) && flags.HasNone(SerializationFlags.SerializedByUnity))
                    {
                        buffer.Append("The member is not being serialized by Unity since ");

                        if (flags.HasAll(SerializationFlags.Property))
                        {
                            buffer.Append("Unity does not serialize properties.");
                        }
                        else if (UnitySerializationUtility.GuessIfUnityWillSerialize(member.GetReturnType()) == false)
                        {
                            buffer.Append("Unity does not support the type.");
                        }
                        else if (flags.HasAll(SerializationFlags.NonSerializedAttribute))
                        {
                            buffer.Append("the [NonSerialized] attribute is defined.");
                        }
                        else if (flags.HasAny(SerializationFlags.Public | SerializationFlags.SerializeFieldAttribute) == false)
                        {
                            buffer.Append("it is neither a public field or has the [SerializeField] attribute.");
                        }
                        else
                        {
                            buffer.Append("# Missing case, please report: " + flags.ToString());
                        }
                    }

                    // Empty the buffer.
                    if (buffer.Length > 0)
                    {
                        notes.Add(buffer.ToString());
                        buffer.Length = 0;
                    }

                    // Why is the value not serialized by Odin?
                    if (flags.HasAll(SerializationFlags.SerializedByOdin) == false)
                    {
                        buffer.Append("Member is not serialized by Odin because ");

                        if ((serializationBackend & SerializationBackendFlags.Odin) != 0)
                        {
                            if (flags.HasAll(SerializationFlags.SerializedByUnity))
                            {
                                buffer.Append("the member is already serialized by Unity. ");
                            }
                        }
                        else
                        {
                            buffer.Append("Odin serialization is not implemented. ");

                            if (flags.HasAll(SerializationFlags.OdinSerializeAttribute))
                            {
                                buffer.Append("The use of [OdinSerialize] attribute is invalid.");
                            }
                        }
                    }
                }
                else // Why not?
                {
                    // Property members with Odin implementation.
                    if (flags.HasAll(SerializationFlags.Property) && serializationBackend.HasAll(SerializationBackendFlags.Odin))
                    {
                        if (flags.HasAll(SerializationFlags.AutoProperty) == false)
                        {
                            buffer.Append("is skipped by Odin because non-auto properties are not serialized. ");

                            if (flags.HasAll(SerializationFlags.SerializeFieldAttribute | SerializationFlags.OdinSerializeAttribute))
                            {
                                buffer.Append("The use of [SerializeField] and [OdinSerialize] attributes is invalid. ");
                            }
                            else if (flags.HasAll(SerializationFlags.OdinSerializeAttribute))
                            {
                                buffer.Append("The use of [OdinSerialize] attribute is invalid. ");
                            }
                            else if (flags.HasAll(SerializationFlags.SerializeFieldAttribute))
                            {
                                buffer.Append("The use of [SerializeField] attribute is invalid. ");
                            }
                            else if (flags.HasAll(SerializationFlags.NonSerializedAttribute))
                            {
                                buffer.Append("The use of [NonSerialized] attribute is unnecessary. ");
                            }
                        }
                        else
                        {
                            buffer.Append("Auto property member is skipped by Odin because ");

                            if (flags.HasNone(SerializationFlags.SerializeFieldAttribute | SerializationFlags.OdinSerializeAttribute))
                            {
                                buffer.Append("neither [SerializeField] nor [OdinSerialize] attributes have been used.");
                            }
                        }
                    }
                    // Property members without Odin implementation.
                    else if (flags.HasAll(SerializationFlags.Property))
                    {
                        buffer.Append("is skipped by Unity because Unity does not serialize properties. ");

                        if (flags.HasAll(SerializationFlags.SerializeFieldAttribute | SerializationFlags.OdinSerializeAttribute))
                        {
                            buffer.Append("The use of [SerializeField] and [OdinSerialize] attributes is invalid. ");
                        }
                        else if (flags.HasAll(SerializationFlags.OdinSerializeAttribute))
                        {
                            buffer.Append("The use of [OdinSerialize] attribute is invalid. ");
                        }
                        else if (flags.HasAny(SerializationFlags.SerializeFieldAttribute))
                        {
                            buffer.Append("The use of [SerializeField] attribute is invalid. ");
                        }

                        if (flags.HasAny(SerializationFlags.NonSerializedAttribute))
                        {
                            buffer.Append("The use of [NonSerialized] attribute is unnecessary.");
                        }
                    }
                    // Field members.
                    else
                    {
                        // Backend ?
                        buffer.Append("is skipped by ");
                        switch (serializationBackend)
                        {
                        case SerializationBackendFlags.Unity:
                            buffer.Append("Unity ");
                            break;

                        case SerializationBackendFlags.Odin:
                            buffer.Append("Odin ");
                            break;

                        case SerializationBackendFlags.UnityAndOdin:
                            buffer.Append("both Unity and Odin ");
                            break;
                        }

                        buffer.Append("because ");

                        if (serializationBackend == SerializationBackendFlags.None)
                        {
                            buffer.Append("there is no serialization backend? ");
                        }
                        else if (flags.HasAll(SerializationFlags.NonSerializedAttribute))
                        {
                            buffer.Append("the [NonSerialized] attribute is defined. ");
                        }
                        else if (flags.HasNone(SerializationFlags.Public | SerializationFlags.SerializeFieldAttribute))
                        {
                            buffer.Append("the field is neither public nor is the [SerializeField] attribute defined. ");
                        }
                        else if (serializationBackend == SerializationBackendFlags.Unity && flags.HasAny(SerializationFlags.Public | SerializationFlags.SerializeFieldAttribute))
                        {
                            buffer.Append("Unity does not support the type " + member.GetReturnType().GetNiceName());
                        }

                        // Empty the buffer.
                        if (buffer.Length > 0)
                        {
                            notes.Add(buffer.ToString());
                            buffer.Length = 0;
                        }

                        // Invalid use of OdinSerialize.
                        if ((serializationBackend & SerializationBackendFlags.Odin) == 0 && flags.HasAll(SerializationFlags.OdinSerializeAttribute))
                        {
                            notes.Add("Odin serialization is not implemented. The use of [OdinSerialize] attribute is invalid."); // Just add this line directly to the notes list.
                        }
                    }

                    // Using both [SerializeField] and [NonSerialized] attributes.
                    if (flags.HasAll(SerializationFlags.SerializeFieldAttribute | SerializationFlags.NonSerializedAttribute) && flags.HasNone(SerializationFlags.OdinSerializeAttribute))
                    {
                        notes.Add("Use of [SerializeField] along with [NonSerialized] attributes is weird. Remove either the [SerializeField] or [NonSerialized] attribute.");
                    }
                }
            }
            else // Custom serialization policy.
            {
                if (flags.HasAll(SerializationFlags.AutoProperty))
                {
                    buffer.Append(flags.HasAll(SerializationFlags.Public) ? "The public auto property " : "The auto property ");
                }
                else if (flags.HasAll(SerializationFlags.Property))
                {
                    buffer.Append(flags.HasAll(SerializationFlags.Public) ? "The public property " : "The property ");
                }
                else
                {
                    buffer.Append(flags.HasAll(SerializationFlags.Public) ? "The public field " : "The field ");
                }
                buffer.AppendFormat("{0} ", member.GetNiceName());

                // Serialized by Unity.
                if (flags.HasAll(SerializationFlags.SerializedByUnity))
                {
                    buffer.Append("is serialized by Unity since ");

                    if (flags.HasAll(SerializationFlags.Field))
                    {
                        switch (flags & (SerializationFlags.Public | SerializationFlags.SerializeFieldAttribute))
                        {
                        case SerializationFlags.Public:
                            buffer.Append("its access modifier is public. ");
                            break;

                        case SerializationFlags.SerializeFieldAttribute:
                            buffer.Append("it the [SerializeField] attribute is defined. ");
                            break;

                        case SerializationFlags.Public | SerializationFlags.SerializeFieldAttribute:
                            buffer.Append("it the [SerializeField] attribute is defined, and it's public. ");
                            break;

                        default:
                            buffer.Append("(MISSING CASE: " + flags.ToString() + ")");
                            break;
                        }
                    }
                    else
                    {
                        buffer.Append("is serialized by Unity? Unity should not be serializing any properties? ");
                    }
                }
                else
                {
                    buffer.Append("is skipped by Unity ");

                    if (flags.HasAll(SerializationFlags.Field))
                    {
                        if (flags.HasNone(SerializationFlags.TypeSupportedByUnity))
                        {
                            buffer.Append("because Unity does not support the type " + member.GetReturnType().GetNiceName());
                        }
                        else if (flags.HasAll(SerializationFlags.NonSerializedAttribute))
                        {
                            buffer.Append("because the [NonSerialized] attribute is defined. ");
                        }
                        else if (flags.HasNone(SerializationFlags.Public | SerializationFlags.SerializeFieldAttribute))
                        {
                            buffer.Append("because the field is not public nor is the [SerializeField] attribute is not defined. ");
                        }
                        else
                        {
                            buffer.Append("(MISSING CASE: " + flags.ToString() + ")");
                        }
                    }
                    else
                    {
                        buffer.Append("because Unity does not serialize properties. ");
                    }
                }

                // Empty the buffer.
                if (buffer.Length > 0)
                {
                    notes.Add(buffer.ToString());
                    buffer.Length = 0;
                }

                // Serialized by Odin with custom serialization policy.
                if (flags.HasAll(SerializationFlags.AutoProperty))
                {
                    buffer.Append("The auto property ");
                }
                else if (flags.HasAll(SerializationFlags.Property))
                {
                    buffer.Append("The property ");
                }
                else
                {
                    buffer.Append("The field ");
                }

                if (flags.HasAll(SerializationFlags.SerializedByOdin))
                {
                    buffer.Append("is serialized by Odin because of custom serialization policy: " + serializationPolicyId);
                }
                else
                {
                    buffer.Append("is skipped by Odin because of custom serialization policy: " + serializationPolicyId);
                }

                // Empty the buffer.
                if (buffer.Length > 0)
                {
                    notes.Add(buffer.ToString());
                    buffer.Length = 0;
                }

                // Serialized by both Unity and Odin.
                if (flags.HasAll(SerializationFlags.SerializedByOdin | SerializationFlags.SerializedByUnity))
                {
                    notes.Add("The member is serialized by both Unity and Odin. Consider ensuring that only one serializer is in use.");
                }
            }

            // Empty the buffer.
            if (buffer.Length > 0)
            {
                notes.Add(buffer.ToString());
                buffer.Length = 0;
            }

            // Add notes on Unity serialization support.
            if (serializationBackend.HasAll(SerializationBackendFlags.UnityAndOdin))
            {
                if (flags.HasAll(SerializationFlags.SerializedByOdin | SerializationFlags.TypeSupportedByUnity) && flags.HasNone(SerializationFlags.SerializedByUnity))
                {
                    buffer.Append("The type " + member.GetReturnType().GetNiceName() + " appears to be supported by Unity. Are you certain that you want to use Odin for serializing?");
                }
                else if (flags.HasAll(SerializationFlags.SerializedByOdin) && flags.HasNone(SerializationFlags.TypeSupportedByUnity))
                {
                    buffer.Append("The type " + member.GetReturnType().GetNiceName() + " is not supported by Unity" + GuessWhyUnityDoesNotSupport(member.GetReturnType()));
                }
            }
            else if (serializationBackend.HasAll(SerializationBackendFlags.Unity) && flags.HasNone(SerializationFlags.TypeSupportedByUnity))
            {
                buffer.Append("The type " + member.GetReturnType().GetNiceName() + " is not supported by Unity" + GuessWhyUnityDoesNotSupport(member.GetReturnType()));
            }

            // Empty the buffer.
            if (buffer.Length > 0)
            {
                notes.Add(buffer.ToString());
                buffer.Length = 0;
            }

            // Implement Odin support.
            if (serializationBackend.HasAll(SerializationBackendFlags.Unity) &&
                serializationBackend.HasNone(SerializationBackendFlags.Odin) &&
                flags.HasNone(SerializationFlags.TypeSupportedByUnity) &&
                flags.HasAny(SerializationFlags.Public | SerializationFlags.SerializeFieldAttribute | SerializationFlags.OdinSerializeAttribute) &&
                flags.HasAny(SerializationFlags.Field | SerializationFlags.AutoProperty))
            {
                string inheritFrom = "You could implement Odin serializing by inheriting " + member.DeclaringType.GetNiceName() + " from ";

                if (typeof(MonoBehaviour).IsAssignableFrom(member.DeclaringType))
                {
                    buffer.Append(inheritFrom + typeof(SerializedMonoBehaviour).GetNiceName());
                }
                else if (UnityNetworkingUtility.NetworkBehaviourType != null && UnityNetworkingUtility.NetworkBehaviourType.IsAssignableFrom(member.DeclaringType))
                {
                    buffer.Append(inheritFrom + " SerializedNetworkBehaviour");
                }
                else if (typeof(Behaviour).IsAssignableFrom(member.DeclaringType))
                {
                    buffer.Append(inheritFrom + typeof(SerializedBehaviour).GetNiceName());
                }
                else if (typeof(ScriptableObject).IsAssignableFrom(member.DeclaringType))
                {
                    buffer.Append(inheritFrom + typeof(SerializedScriptableObject).GetNiceName());
                }
            }

            // Empty the buffer.
            if (buffer.Length > 0)
            {
                notes.Add(buffer.ToString());
                buffer.Length = 0;
            }

            // Recommend using fields instead of auto properties.
            if (serializationBackend.HasAll(SerializationBackendFlags.Odin) &&
                flags.HasAll(SerializationFlags.AutoProperty | SerializationFlags.SerializedByOdin))
            {
                buffer.Append("It's recommend to use backing fields for serializing instead of auto-properties.");
            }

            // Empty the buffer.
            if (buffer.Length > 0)
            {
                notes.Add(buffer.ToString());
                buffer.Length = 0;
            }

            return(notes.ToArray());
        }
        private static MemberSerializationInfo CreateInfoFor(MemberInfo member, SerializationBackendFlags serializationBackend, bool serializeUnityFields, ISerializationPolicy serializationPolicy)
        {
            SerializationFlags flags = 0;

            // Is the member a field, property or auto-property?
            if (member is FieldInfo)
            {
                var f = member as FieldInfo;
                flags |= SerializationFlags.Field;

                if (f.IsPublic)
                {
                    flags |= SerializationFlags.Public;
                }
            }
            else if (member is PropertyInfo)
            {
                var p = member as PropertyInfo;
                flags |= SerializationFlags.Property;

                if (p.GetGetMethod() != null && p.GetGetMethod().IsPublic || p.GetSetMethod() != null && p.GetSetMethod().IsPublic)
                {
                    flags |= SerializationFlags.Public;
                }
                if (p.IsAutoProperty())
                {
                    flags |= SerializationFlags.AutoProperty;
                }
            }

            // Is the default serialization policy in use?
            if (serializationPolicy != null && serializationPolicy.ID == SerializationPolicies.Unity.ID)
            {
                flags |= SerializationFlags.DefaultSerializationPolicy;
            }

            // Will Unity serialize the member?
            if ((serializationBackend & SerializationBackendFlags.Unity) != 0 && UnitySerializationUtility.GuessIfUnityWillSerialize(member))
            {
                flags |= SerializationFlags.SerializedByUnity;
            }

            // Will Odin serialize the member?
            if ((serializationBackend & SerializationBackendFlags.Odin) != 0 && UnitySerializationUtility.OdinWillSerialize(member, serializeUnityFields, serializationPolicy))
            {
                flags |= SerializationFlags.SerializedByOdin;
            }

            // Does the member have a SerializeField attribute?
            if (member.IsDefined <SerializeField>())
            {
                flags |= SerializationFlags.SerializeFieldAttribute;
            }

            // Does the member have a OdinSerialize attribute?
            if (member.IsDefined <OdinSerializeAttribute>())
            {
                flags |= SerializationFlags.OdinSerializeAttribute;
            }

            // Does the member have a NonSerialized attribute?
            if (member.IsDefined <NonSerializedAttribute>())
            {
                flags |= SerializationFlags.NonSerializedAttribute;
            }

            // Does Unity support serializing the type?
            if (serializationBackend.HasAll(SerializationBackendFlags.Unity) && UnitySerializationUtility.GuessIfUnityWillSerialize(member.GetReturnType()))
            {
                flags |= SerializationFlags.TypeSupportedByUnity;
            }

            return(new MemberSerializationInfo(member, CreateNotes(member, flags, serializationBackend, serializationPolicy.ID), flags, serializationBackend));
        }