Oh, yay, I get to show off something I recently wrote for something else! :)
Caveat: This code is not the most efficient in the world, and it's VERY poorly commented - it was for a personal project, and I already knew how it worked - but I think it'll get you what you're after...
The method you'd be most interested in would be
public static Tuple> GetCommonBases(Type left, Type right)
The Tuple returned is
A quick summary: this class, when given a type, does the following:
Reverse walks up the given type until it hits no more base types, pushing each into a 'working stack'
Pops each base type off the working stack, inserting it into a tree-like structure; If the type implements any interfaces, it adds nodes for those interface types as well
the helper method GetCommonBases
creates one of these TypeTree
structures for the first type, then 'merges' in the type tree for the other given type: it does so by walking down common base types until it finds a point where you have a common base type between the two types, at which point two branches of the tree are formed. It then "drills down" to each type from the root (i.e., System.Object
), then finds the first point of deviation. The parent of this point of deviation is the Common base type.
The interfaces part relies on the definition of Interfaces
, which "inherits" any Interface nodes for any ancestor. The GetCommonBases
method pulls a list of any interfaces implemented by the two passed in types and returns an intersection of these two lists - that is, a set of interfaces that both passed in types implement.
the method then returns these two bits of information as a Tuple>
, where the first item is the common base type, if any, and the second item is the intersection of common interfaces
public class TypeTree
{
private TypeTree()
{
Children = new List();
}
public TypeTree(Type value)
: this()
{
// Get to the basest class
var typeChain = GetTypeChain(value).ToList();
Value = typeChain.First();
foreach (var type in typeChain.Skip(1))
{
Add(type);
}
}
public Type Value { get; private set; }
public TypeTree Parent { get; private set; }
public List Children { get; private set; }
public IEnumerable Interfaces
{
get
{
var myInterfaces = Children.Where(c => c.Value.IsInterface);
return Parent == null ? myInterfaces : myInterfaces.Concat(Parent.Interfaces).Distinct();
}
}
public TypeTree Find(Type type)
{
if (Value == type)
return this;
return Children.Select(child => child.Find(type)).FirstOrDefault(found => found != null);
}
public TypeTree Add(Type type)
{
TypeTree retVal = null;
if (type.IsInterface)
{
if (Value.GetInterfaces().Contains(type))
{
retVal = new TypeTree { Value = type, Parent = this };
Children.Add(retVal);
return retVal;
}
}
var typeChain = GetTypeChain(type);
var walkTypes =
from baseType in typeChain
let alreadyExists = Value == baseType || Children.Any(c => c.Value == baseType)
where !alreadyExists
select baseType;
foreach (var baseType in walkTypes)
{
if (baseType.BaseType == Value)
{
// Add this as a child of the current tree
retVal = new TypeTree { Value = baseType, Parent = this };
Children.Add(retVal);
}
if (Value.IsAssignableFrom(baseType))
{
// we can add this as a child, potentially
retVal = Children.Aggregate(retVal, (current, child) => child.Add(baseType) ?? current);
}
// add interfaces
var interfaces = baseType.GetInterfaces().Where(i => i != type);
foreach (var intType in interfaces)
{
(retVal ?? this).Add(intType);
}
}
return retVal;
}
public override string ToString()
{
var childTypeNames = Children.Select(c => c.ToString()).Distinct();
return string.Format("({0} {1})", Value.Name, string.Join(" ", childTypeNames));
}
public static Tuple> GetCommonBases(Type left, Type right)
{
var tree = new TypeTree(left);
tree.Add(right);
var findLeft = tree.Find(left);
var findRight = tree.Find(right);
var commonInterfaces =
findLeft.Interfaces.Select(i => i.Value)
.Intersect(findRight.Interfaces.Select(i => i.Value))
.Distinct();
var leftStack = new Stack();
var temp = findLeft;
while (temp != null)
{
leftStack.Push(temp);
temp = temp.Parent;
}
var rightStack = new Stack();
temp = findRight;
while (temp != null)
{
rightStack.Push(temp);
temp = temp.Parent;
}
var zippedPaths = leftStack.Zip(rightStack, Tuple.Create);
var result = zippedPaths.TakeWhile(tup => tup.Item1.Value == tup.Item2.Value).Last();
return Tuple.Create(result.Item1.Value, commonInterfaces);
}
private static IEnumerable GetTypeChain(Type fromType)
{
var typeChain = new Stack();
var temp = fromType;
while (temp != null)
{
typeChain.Push(temp);
temp = temp.BaseType;
}
return typeChain;
}
}