64

How can I compare the types of two objects declared as type.

I want to know if two objects are of the same type or from the same base class.

Any help is appreciated.

e.g.

private bool AreSame(Type a, Type b) {

}
B Z
  • 9,363
  • 16
  • 67
  • 91

4 Answers4

93

Say a and b are the two objects. If you want to see if a and b are in the same inheritance hierarchy, then use Type.IsAssignableFrom:

var t = a.GetType();
var u = b.GetType();

if (t.IsAssignableFrom(u) || u.IsAssignableFrom(t)) {
  // x.IsAssignableFrom(y) returns true if:
  //   (1) x and y are the same type
  //   (2) x and y are in the same inheritance hierarchy
  //   (3) y is implemented by x
  //   (4) y is a generic type parameter and one of its constraints is x
}

If you want to check if one is a base class of the other, then try Type.IsSubclassOf.

If you know the specific base class, then just use the is keyword:

if (a is T && b is T) {
  // Objects are both of type T.
}

Otherwise, you'll have to walk the inheritance hierarchy directly.

John Feminella
  • 303,634
  • 46
  • 339
  • 357
  • +1 I wasn't aware of that handy function IsAssignableFrom, i'll have to keep that in mind. – James Apr 02 '09 at 04:09
  • There's quite a lot of useful stuff in the Reflection hierarchy! It's interesting just to browse the members list -- chances are pretty good you'll find what you need. Do read the docs, though. For example, IsAssignableFrom would return true if t was a generic type param and u was a constraint. – John Feminella Apr 02 '09 at 04:12
  • Just FYI, though, this will not return true if the two classes have the same base class. – Adam Robinson Apr 02 '09 at 12:59
  • @AdamRobinson: I'm not sure that's a useful distinction. Every reference type comes from System.Object, so they'd *all* have the same base class. – John Feminella Apr 02 '09 at 15:16
  • @John: See my answer. Obviously everything derives from object. However, the poster indicated that it would be of some value to him to know if the classes had the same base class (to which I said you'd have to decide how far up you'd go), hence my response. – Adam Robinson Apr 02 '09 at 15:44
  • Do these operations perform well? Could you expand upon the performance hits, if any? – bradj Feb 26 '14 at 16:21
32

There's a bit of a problem with this idea, though, as every object (and, indeed, every type) DOES have a common base class, Object. What you need to define is how far up the chain of inheritance you want to go (whether it's they're either the same or they have the same immediate parent, or one is the immediate parent of the other, etc.) and do your checks that way. IsAssignableFrom is useful for determining if types are compatible with one another, but won't fully establish if they have the same parent (if that's what you're after).

If your strict criteria is that the function should return true if...

  • The types are identical
  • One type is the parent (immediate or otherwise) of the other
  • The two types have the same immediate parent

You could use

private bool AreSame(Type a, Type b) 
{
    if(a == b) return true; // Either both are null or they are the same type

    if(a == null || b == null) return false; 

    if(a.IsSubclassOf(b) || b.IsSubclassOf(a)) return true; // One inherits from the other

    return a.BaseType == b.BaseType; // They have the same immediate parent
}
Adam Robinson
  • 182,639
  • 35
  • 285
  • 343
  • great sample and explanation. +1 – B Z Apr 02 '09 at 14:49
  • I wished there was a way to share an accepted answer. Thx again. – B Z Apr 02 '09 at 16:09
  • @Adam: I wonder if there isn't a way to combine both of our approaches. You could keep a list of Types that resulted from taking BaseType.GetType() on both a and b until you either (1) reached System.Object (or some more restricted derived type) [fail] or (2) there's a match [pass]. – John Feminella Apr 02 '09 at 19:15
  • Oh, definitely. That's why I was saying you had to decide how much farther up the stack you wanted to travel...you can also run into general system types like MarshalByRefObject, but yeah, you can definitely travel farther up the stack. – Adam Robinson Apr 02 '09 at 20:42
  • It's probably easier just to do a while() loop on a Type variable, getting the base type of either of the variables (shouldn't have to do both I wouldn't think) and restrict it somehow, either by not allowing any base types in system assemblies or a number of "hops". – Adam Robinson Apr 02 '09 at 20:43
  • Nice concise code. However, I am not sure how you can make either a or b as null. Type cannot be null, so 2nd check will not be useful. – Jeson Martajaya Nov 04 '19 at 07:03
13

You can also use the "IS" keyword, if you expect the two object instances to be of a certain type. This will also work for comparing sub-classes to parent classes and also classes that implement interfaces and so forth. This will not work for types of type Type though.

if (objA Is string && objB Is string)
// they are the same.

public class a {}

public class b : a {}

b objb = new b();

if (objb Is a)
// they are of the same via inheritance
James
  • 12,636
  • 12
  • 67
  • 104
2

I tried out the following with a hierarchy using both interfaces and concrete classes. It walks up the base class chain for one of the types till it reaches "object" at which we check if the current destination type is assignable to the source type. We also check if the types have a common interface. if they do then they 'AreSame'

Hope this helps.

 public interface IUser
{
     int ID { get; set; }
     string Name { get; set; }
}

public class NetworkUser : IUser
{
    public int ID
    {
        get;
        set;
    }

    public string Name
    {
        get;
        set;
    }
}

public class Associate : NetworkUser,IUser
{
    #region IUser Members

    public int ID
    {
        get;
        set;
    }

    public string Name
    {
        get;
        set;
    }

    #endregion
}

public class Manager : NetworkUser,IUser
{
    #region IUser Members

    public int ID
    {
        get;
        set;
    }

    public string Name
    {
        get;
        set;
    }

    #endregion
}


public class Program
{

    public static bool AreSame(Type sourceType, Type destinationType)
    {
        if (sourceType == null || destinationType == null)
        {
            return false;
        }

        if (sourceType == destinationType)
        {
            return true;
        }

        //walk up the inheritance chain till we reach 'object' at which point check if 
    //the current destination type is assignable from the source type      
    Type tempDestinationType = destinationType;
        while (tempDestinationType.BaseType != typeof(object))
        {
            tempDestinationType = tempDestinationType.BaseType;
        }
        if( tempDestinationType.IsAssignableFrom(sourceType))
        {
            return true;
        }

        var query = from d in destinationType.GetInterfaces() join s in sourceType.GetInterfaces()
                    on d.Name equals s.Name
                    select s;
        //if the results of the query are not empty then we have a common interface , so return true 
    if (query != Enumerable.Empty<Type>())
        {
            return true;
        }
        return false;            
    }

    public static void Main(string[] args)
    {

        AreSame(new Manager().GetType(), new Associate().GetType());
    }
}
Abhijeet Patel
  • 6,562
  • 8
  • 50
  • 93