6

I'm working on a game engine in C#. The class I'm working on is called CEntityRegistry, and its job is to keep track of the many instances of CEntity in the game. My goal is to be able to query the CEntityRegistry with a given type, and get a list of each CEntity of that type.

What I'd like to do, therefore, is maintain a map:

private IDictionary<Type, HashSet<CEntity>> m_TypeToEntitySet;

And update the registry thusly:

private void m_UpdateEntityList()
        {
            foreach (CEntity theEntity in m_EntitiesToRemove.dequeueAll())
            {
                foreach (HashSet<CEntity> set in m_TypeToEntitySet.Values)
                {
                    if (set.Contains(theEntity))
                        set.Remove(theEntity);
                }
            }
            foreach (CEntity theEntity in m_EntitiesToAdd.dequeueAll())
            {
                Type entityType = theEntity.GetType();
                foreach (Type baseClass in entityType.GetAllBaseClassesAndInterfaces())
                  m_TypeToEntitySet[baseClass].Add(theEntity);

            }
        }

The problem I have is that there is no function Type.GetAllBaseClassesAndInterfaces- How would I go about writing it?

Mark P Neyer
  • 1,009
  • 2
  • 8
  • 19
  • but surely you don't want an instance of a type to be counted as as an instance all it's sub-types and interfaces as well? – Mitch Wheat Dec 01 '09 at 02:40
  • 2
    Have you considered that using Type.IsAssignableFrom(Type) might make your life a little simpler than trying to map the entire inheritance hierarchy of every CEntity? Not sure what exactly how exactly you plan on using the repository, but it may be something to look at. – Rory Dec 01 '09 at 02:48
  • 3
    I wonder if your energy wouldn't be better spent asking yourself why this is needed and if there's a more straightforward approach that doesn't require reflection. Also, keeping a dictionary of all those objects could (if you implement it carelessly) prevent them from ever being garbage collected and cause a memory leak in your program. – Nate C-K Dec 01 '09 at 02:49

5 Answers5

20

You could write an extension method like this:

public static IEnumerable<Type> GetBaseTypes(this Type type) {
    if(type.BaseType == null) return type.GetInterfaces();

    return Enumerable.Repeat(type.BaseType, 1)
                     .Concat(type.GetInterfaces())
                     .Concat(type.GetInterfaces().SelectMany<Type, Type>(GetBaseTypes))
                     .Concat(type.BaseType.GetBaseTypes());
}
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • Say I've got "class One : IOne {}" "class Two : One {}" As written, run against typeof(Two).GetBaseTypes() will return multiples of IOne even though it's only implemented once. Two is an IOne so it returns it even though it doesn't have it explicitly implemented. – claudekennilol May 08 '14 at 18:31
  • 1
    @claudekennilol: Then add `.Distinct()`. See also http://blogs.msdn.com/b/ericlippert/archive/2011/04/04/so-many-interfaces.aspx and http://blogs.msdn.com/b/ericlippert/archive/2011/12/08/so-many-interfaces-part-two.aspx – SLaks May 08 '14 at 19:31
  • @SLaks You don't need to look for interfaces recursively. One call to GetInterfaces will get all superinterfaces. – N73k Jul 10 '19 at 17:11
9

More refined answer based on one from SLaks would be:

public static IEnumerable<Type> GetBaseClassesAndInterfaces(this Type type)
{
    return type.BaseType == typeof(object) 
        ? type.GetInterfaces()
        : Enumerable
            .Repeat(type.BaseType, 1)
            .Concat(type.GetInterfaces())
            .Concat(type.BaseType.GetBaseClassesAndInterfaces())
            .Distinct();
}
dadhi
  • 4,807
  • 19
  • 25
8

Type has a property BaseType and a method FindInterfaces.

https://msdn.microsoft.com/en-us/library/system.type.aspx

So actually, it almost does have Type.GetAllBaseClassesAndInterfaces, but you have to make two calls instead of one.

Nate C-K
  • 5,744
  • 2
  • 29
  • 45
  • He wants to recurse through the type hierarchy (Hence GetAllBaseClass **es**), so it's not that simple. – SLaks Dec 01 '09 at 02:42
  • 1
    I guess I'm not sure whether his question was about what method/property to use, or how to write a function that will traverse the inheritance tree. I had assumed the former. – Nate C-K Dec 01 '09 at 02:44
  • It might be simpler to make the CEntity constructor register itself with that dictionary instead. Then you don't have to query the inheritance tree for each object. Heavy use of reflection is often an indication that something is wrong with your design. – Nate C-K Dec 01 '09 at 02:52
3

Here. The previous answers have problems. Also, this answer doesn't need "Distinct". The values are already distinct. And this one is more efficient.

    public static IEnumerable<Type> GetBaseTypes(this Type type, bool bIncludeInterfaces = false)
    {
        if (type == null)
            yield break;

        for (var nextType = type.BaseType; nextType != null; nextType = nextType.BaseType)
            yield return nextType;

        if (!bIncludeInterfaces)
            yield break;

        foreach (var i in type.GetInterfaces())
            yield return i;
    }
N73k
  • 527
  • 8
  • 20
0

Use this code:

Func<Type, List<Type>> f = ty =>
{
    var tysReturn = new List<Type>();
    if (ty.BaseType != null)
    {
        tysReturn.Add(ty.BaseType);
    }
    tysReturn.AddRange(ty.GetInterfaces());
    return tysReturn;
};

The function f will take a Type and return a list of its base type plus interfaces.

Hope it helps.

CesarGon
  • 15,099
  • 6
  • 57
  • 85