static void AddClassDumpData( Type type, ClassDumpData classDumpData, bool replace) { if (type == null) { throw new ArgumentNullException(nameof(type)); } TypesDumpDataSync.EnterWriteLock(); try { if (!replace && TypesDumpData.TryGetValue(type, out var dumpData)) { if (dumpData == classDumpData) { return; } throw new InvalidOperationException( $"The type {type.FullName} is already associated with metadata type {TypesDumpData[type].Metadata.FullName} and a DumpAttribute instance."); } TypesDumpData[type] = classDumpData; } finally { TypesDumpDataSync.ExitWriteLock(); } }
internal void DumpObject( object obj, Type dumpMetadata = null, DumpAttribute dumpAttribute = null, DumpState parentState = null) { if (Writer.DumpedBasicValue(obj, dumpAttribute) || Writer.DumpedBasicNullable(obj, dumpAttribute)) // incl. null { return; } // resolve the class metadata and the dump attribute ClassDumpData classDumpData; var objectType = obj.GetType(); if (dumpMetadata == null) { classDumpData = ClassMetadataResolver.GetClassDumpData(objectType); if (dumpAttribute != null) { classDumpData.DumpAttribute = dumpAttribute; } } else { classDumpData = new ClassDumpData(dumpMetadata, dumpAttribute); } // if we're too deep - stop here. if (_maxDepth == int.MinValue) { _maxDepth = classDumpData.DumpAttribute.MaxDepth; } if (_maxDepth < 0) { Writer.Write(Resources.DumpReachedMaxDepth); return; } // save here the IsSubExpression flag as it will change if obj is Expression and IsSubExpression==false. // but in the end of this method restore the value from this local variable. See below. var isSubExpressionStore = IsSubExpression; // slow dump vs. run script? bool isTopLevelObject = parentState == null; var buildScript = UseDumpScriptCache && !obj.IsDynamicObject(); Script script = null; using (var state = new DumpState(this, obj, classDumpData, buildScript)) { if (buildScript) { // does the script exist or is it in process of building if (DumpScriptCache.TryFind(this, obj, classDumpData, out script)) { if (script != null) { if (isTopLevelObject) { Writer.Indent(_indentLevel, _indentSize) .WriteLine(); } script(obj, classDumpData, this, state); } return; } DumpScriptCache.BuildingScriptFor(this, objectType, classDumpData); } else { if (isTopLevelObject) { Writer.Indent(_indentLevel, _indentSize) .WriteLine(); } } if (!state.DumpedAlready()) // the object has been dumped already (block circular and repeating references) { // this object will be dumped below. // Add it to the dumped objects now so that if nested property refers back to it, it won't be dumped in an infinite recursive chain. DumpedObjects.Add(new DumpedObject(obj, objectType)); if (!state.DumpedCollection(classDumpData.DumpAttribute, false)) // custom collections are dumped after dumping all other properties (see below * ) { Stack <DumpState> statesWithRemainingProperties = new Stack <DumpState>(); Queue <DumpState> statesWithTailProperties = new Queue <DumpState>(); // recursively dump all properties with non-negative order in class inheritance descending order (base classes' properties first) // and if there are more properties to be dumped put them in the stack if (!DumpedTopProperties(state, statesWithRemainingProperties)) { // dump all properties with negative order in class ascending order (derived classes' properties first) DumpRemainingProperties(statesWithRemainingProperties, statesWithTailProperties); // dump all properties with Order=int.MinValue in ascending order (derived classes' properties first) DumpTailProperties(statesWithTailProperties); // * if the object implements IEnumerable and the state allows it - dump the elements. state.DumpedCollection(classDumpData.DumpAttribute, true); } // we are done dumping state.Unindent(); } } if (buildScript && state.DumpScript != null) { script = DumpScriptCache.Add(this, objectType, classDumpData, state.DumpScript); } if (!buildScript) { if (isTopLevelObject) { Writer.Unindent(_indentLevel, _indentSize); } } else { if (script != null) { if (isTopLevelObject) { Writer.Indent(_indentLevel, _indentSize) .WriteLine(); } script(obj, classDumpData, this, state); } } // restore here the IsSubExpression flag as it have changed if obj is Expression and IsSubExpression==false. IsSubExpression = isSubExpressionStore; } }