internal void WriteValueWithoutAdapters <TValue>(TValue value, bool isRoot) { if (RuntimeTypeInfoCache <TValue> .IsEnum) { BinarySerialization.WritePrimitiveUnsafe(m_Stream, ref value, Enum.GetUnderlyingType(typeof(TValue))); return; } if (RuntimeTypeInfoCache <TValue> .CanBeNull && null == value) { m_Stream->Add(k_TokenNull); return; } if (RuntimeTypeInfoCache <TValue> .IsMultidimensionalArray) { // No support for multidimensional arrays yet. This can be done using adapters for now. m_Stream->Add(k_TokenNull); return; } if (RuntimeTypeInfoCache <TValue> .IsNullable) { m_Stream->Add(k_TokenNone); var underlyingType = Nullable.GetUnderlyingType(typeof(TValue)); if (RuntimeTypeInfoCache.IsContainerType(underlyingType)) { // IMPORTANT: Do NOT add a token to the stream since we are triggering a re-entrance on the same object here. // Unpack Nullable<T> as T var underlyingValue = Convert.ChangeType(value, underlyingType); if (!PropertyContainer.TryAccept(this, ref underlyingValue, out var errorCode)) { switch (errorCode) { case VisitErrorCode.NullContainer: throw new ArgumentNullException(nameof(value)); case VisitErrorCode.InvalidContainerType: throw new InvalidContainerTypeException(value.GetType()); case VisitErrorCode.MissingPropertyBag: throw new MissingPropertyBagException(value.GetType()); default: throw new Exception($"Unexpected {nameof(VisitErrorCode)}=[{errorCode}]"); } } } else { BinarySerialization.WritePrimitiveBoxed(m_Stream, value, underlyingType); } return; } if (!RuntimeTypeInfoCache <TValue> .IsValueType) { #if !UNITY_DOTSPLAYER if (!(isRoot && m_DisableRootAdapters) && value is UnityEngine.Object unityEngineObject) { // Special path for polymorphic unity object references. m_Stream->Add(k_TokenUnityEngineObjectReference); (m_Adapters.Internal as Adapters.Contravariant.IBinaryAdapter <UnityEngine.Object>).Serialize(new BinarySerializationContext <TValue>(this, default, value, isRoot), unityEngineObject);
void WriteValue <TValue>(TValue value, bool isRoot = false) { var runAdapters = !(isRoot && m_DisableRootAdapters); if (runAdapters && m_Adapters.TrySerialize(m_Stream, ref value)) { return; } if (RuntimeTypeInfoCache <TValue> .IsEnum) { BinarySerialization.WritePrimitiveUnsafe(m_Stream, ref value, Enum.GetUnderlyingType(typeof(TValue))); return; } if (RuntimeTypeInfoCache <TValue> .CanBeNull && null == value) { m_Stream->Add(k_TokenNull); return; } if (RuntimeTypeInfoCache <TValue> .IsNullable) { m_Stream->Add(k_TokenNone); BinarySerialization.WritePrimitiveBoxed(m_Stream, value, Nullable.GetUnderlyingType(typeof(TValue))); return; } if (!RuntimeTypeInfoCache <TValue> .IsValueType) { #if !UNITY_DOTSPLAYER if (runAdapters && value is UnityEngine.Object unityEngineObject) { // Special path for polymorphic unity object references. m_Stream->Add(k_TokenUnityEngineObjectReference); m_Adapters.TrySerialize(m_Stream, ref unityEngineObject); return; } #endif if (null != m_SerializedReferences && !value.GetType().IsValueType) { // At this point we don't know if an object will reference this value. // To avoid a second visitation pass we always create an entry for each managed object reference. var id = m_SerializedReferences.AddSerializedReference(value); if (!m_SerializedReferences.SetSerialized(value)) { // This is the second time encountering this object during serialization. // We instead of writing out the object we simply write the id. m_Stream->Add(k_TokenSerializedReference); m_Stream->Add(id); return; } } // This is a very common case. At serialize time we are serializing something that is polymorphic or an object // However at deserialize time the user known the System.Type, we can avoid writing out the fully qualified type name in this case. var isRootAndTypeWasGiven = isRoot && null != m_SerializedType; if (typeof(TValue) != value.GetType() && !isRootAndTypeWasGiven) { m_Stream->Add(k_TokenPolymorphic); m_Stream->Add(value.GetType().AssemblyQualifiedName); } else { m_Stream->Add(k_TokenNone); } } if (RuntimeTypeInfoCache <TValue> .IsObjectType && !RuntimeTypeInfoCache.IsContainerType(value.GetType())) { BinarySerialization.WritePrimitiveBoxed(m_Stream, value, value.GetType()); return; } if (!PropertyContainer.Visit(ref value, this, out var errorCode)) { switch (errorCode) { case VisitErrorCode.NullContainer: throw new ArgumentNullException(nameof(value)); case VisitErrorCode.InvalidContainerType: throw new InvalidContainerTypeException(value.GetType()); case VisitErrorCode.MissingPropertyBag: throw new MissingPropertyBagException(value.GetType()); default: throw new Exception($"Unexpected {nameof(VisitErrorCode)}=[{errorCode}]"); } } }