private static TypeInheritance GetInheritanceTree(this Type type, int distance) { var list = new TypeInheritance(type, distance); // Gives us a map of Interface + All ancestor interfaces in the entire hierarchy up. var interfaces = type .GetInterfaces() .Select(i => new { Interface = i, Ancestors = i.GetInterfaces().Traverse(TraverseKind.BreadthFirst, n => n.GetInterfaces()) }); if (type.IsClass) { if (type.BaseType != null) list.Inheritance.Add(GetInheritanceTree(type.BaseType, distance + 1)); list.Inheritance.AddRange(type // Add all interfaces of the type, but .GetInterfaces() // See if the map gives us where the interface members are implemented .Select(i => new { Interface = i, Map = type.GetInterfaceMap(i) }) // Either it is a marker interface, or all members are declared by the type. // (explicit interface implementation or otherwise we are the first class in the hierarchy to introduce the interface). .Where(i => // Detect marker interfaces separately, and add them always as long as they don't show up in others upward. (!i.Map.TargetMethods.Any() && !interfaces.SelectMany(n => n.Ancestors).Any(t => t == i.Interface)) || // For interfaces with members, we can get the map and check if they are declared in the current type. // Note that this brings into this type the interfaces that have a completely overriden implementation (our intended design). (i.Map.TargetMethods.Any() && i.Map.TargetMethods.All(m => m.DeclaringType == type))) .Select(i => GetInheritanceTree(i.Interface, distance + 1))); } else { // Then we only add those interfaces that do not show up as ancestors in any other // interface in the list. list.Inheritance.AddRange(interfaces .Select(i => i.Interface) .Where(i => !interfaces.SelectMany(n => n.Ancestors).Any(t => t == i)) .Select(i => GetInheritanceTree(i, distance + 1))); } return list; }
private static void PrintTypeList(TypeInheritance list, int indentLevel) { Console.WriteLine( Enumerable.Range(0, indentLevel).Aggregate("", (s, level) => s += '\t') + list.Type.Name + " (" + list.Distance + ")"); indentLevel++; foreach (var inner in list.Inheritance) { PrintTypeList(inner, indentLevel); } }