private void SerialiseObjectFieldsAndProperties( object value, Stack <object> parentsIfReferenceReuseDisallowed, Dictionary <object, int> objectHistoryIfReferenceReuseAllowed, HashSet <int> deferredInitialisationObjectReferenceIDsIfSupported, Dictionary <Type, Action <object> > generatedMemberSetters, ISerialisationTypeConverter[] typeConverters, IWrite writer) { // It may be possible for a "type generator" to be created for some types (generally simple types that won't require any nested Serialise calls that involve tracking // parentsIfReferenceReuseDisallowed or objectHistoryIfReferenceReuseAllowed), so check that first. There are three cases; 1. we don't have any type generator data // about the current type, 2. we have tried to retrieve a type generator before and got back null (meaning that this type does not match the writer's conditions // for being able to create a type generator) and 3. we have successfully created a type generator before. If it's case 3 then we'll use that type generator // instead of enumerating fields below but if it's case 1 or 2 then we'll have to do that work (but if it's case 1 then we'll try to find out whether it's // possible to create a type generator at the bottom of this method). var valueType = value.GetType(); var haveTriedToGenerateMemberSetterBefore = generatedMemberSetters.TryGetValue(valueType, out var memberSetter); if (haveTriedToGenerateMemberSetterBefore && (memberSetter != null)) { memberSetter(value); return; } // Write out all of the data for the value var(fields, properties) = _typeAnalyser.GetFieldsAndProperties(valueType); for (var i = 0; i < fields.Length; i++) { var field = fields[i]; if (writer.FieldName(field.Member, valueType)) { if (parentsIfReferenceReuseDisallowed != null) { parentsIfReferenceReuseDisallowed.Push(value); } var fieldValue = field.Reader(value); Serialise( fieldValue, fieldValue?.GetType() ?? field.Member.FieldType, false, // populatingDeferredObject can only refer to the current referene and would never be propagated to member references parentsIfReferenceReuseDisallowed, objectHistoryIfReferenceReuseAllowed, deferredInitialisationObjectReferenceIDsIfSupported, generatedMemberSetters, typeConverters, writer ); if (parentsIfReferenceReuseDisallowed != null) { parentsIfReferenceReuseDisallowed.Pop(); } } } for (var i = 0; i < properties.Length; i++) { var property = properties[i]; if (writer.PropertyName(property.Member, valueType)) { if (parentsIfReferenceReuseDisallowed != null) { parentsIfReferenceReuseDisallowed.Push(value); } var propertyValue = property.Reader(value); Serialise( propertyValue, propertyValue?.GetType() ?? property.Member.PropertyType, false, // populatingDeferredObject can only refer to the current referene and would never be propagated to member references parentsIfReferenceReuseDisallowed, objectHistoryIfReferenceReuseAllowed, deferredInitialisationObjectReferenceIDsIfSupported, generatedMemberSetters, typeConverters, writer ); if (parentsIfReferenceReuseDisallowed != null) { parentsIfReferenceReuseDisallowed.Pop(); } } } // If we have tried before to create a type generator for this type and were unsuccessful then there is nothing more to do.. if (haveTriedToGenerateMemberSetterBefore) { return; } // .. but if we HAVEN'T tried to create a type generator before then ask the writer if it's able to do so (this is done after the first time that an instance of // the type has been fully serialised so that the writer has a chance to create any Name Reference IDs that it might want to use for the member names and potentially // have done some other forms of caching) generatedMemberSetters[valueType] = writer.TryToGenerateMemberSetter(valueType); }