/// <summary>Traverses the object graph rooted at <c>obj</c> by reflecting over the objects in the object's fields, /// their fields, etc. For each object visited in the graph, calls objFilter. If objFilter /// returns false, the object passed to objFilter isn't recursed, else it is. /// /// If a non-null value is provided for <c>parent</c>, any references to the <c>parent</c> object are /// ignored and are neither traversed nor passed to objFilter. /// /// <c>cookie</c> is a caller-defined state object that is passed to objFilter directly. /// </summary> /// /// <param name="cookie"></param> /// <param name="obj"></param> /// <param name="parent"></param> /// <param name="objFilter"></param> /// /// <returns>A list of all the objects in the graph, not including those that were excluded /// by objFilter. This list will not contain duplicates, even if an object /// was encountered multiple times during the recursion.</returns> public ICollection RecurseObjectGraph(Object cookie, Object obj, Object parent, ProcessObjectFilterDelegate objFilter) { if (obj == null) { throw new ArgumentNullException("obj"); } if (objFilter == null) { throw new ArgumentNullException("obj"); } //Start the list of objects ArrayList objList = new ArrayList(); RecurseObjectGraphInternal(objList, cookie, null, obj, parent, objFilter); return objList; }
/// <summary>Internal recursive function that actually does the recursion</summary> /// /// <param name="cookie"></param> /// <param name="field"></param> /// <param name="obj"></param> /// <param name="parent"></param> /// <param name="objFilter"></param> private void RecurseObjectGraphInternal(IList objList, Object cookie, FieldInfo field, Object obj, Object parent, ProcessObjectFilterDelegate objFilter) { //First, pass this object through the object processor. If it indicates //not to recurse further, stop now if (!objFilter(cookie, field, obj, parent)) { return; } //Add this object to the list of objects that have been processed objList.Add(obj); //Get all the fields for this type, and for each one, process its object graph foreach (FieldInfo fi in GetTypeFields(obj.GetType())) { //Process the value of the field RecurseFieldValue(objList, cookie, fi, fi.GetValue(obj), obj, parent, objFilter); } if (obj is Array) { //An array object. Process each of its elements as well foreach (Object arrayObj in (Array)obj) { RecurseFieldValue(objList, cookie, field, arrayObj, obj, parent, objFilter); } } }
/// Encapsulates the logic to recurse the (potentially null) value of a field private void RecurseFieldValue(IList objList, Object cookie, FieldInfo field, Object fieldValue, Object obj, Object parent, ProcessObjectFilterDelegate objFilter) { //If the field value is null, ignore it if (fieldValue == null) { return; } //If this is the current object's parent, ignore it, since it will //already have been processed if (fieldValue == parent) { return; } //If this is already in the list of objects in the graph, don't process //it again if (objList.Contains(fieldValue)) { return; } //Process this object's graph RecurseObjectGraphInternal(objList, cookie, field, fieldValue, obj, objFilter); }