Saturday, February 16, 2013

C#–Determining if an object implements or derives from a generic class or interface

Imagine you have a generic class and interface that are defined as follows:

public abstract class MyGenericClass<T>
{
}
public interface IMyGenericInterface<T>
{
}

If you were to derive or implement from the above class/interface, you will find that you cannot use the “is” keyword to determine if that class implements or derives from the above interface. Here is what I mean: If I were to implement/derive from the base class like so:

public class MyInt:MyGenericClass<int>
{
}

public class MyInterfacedInt:IMyGenericInterface<int>
{
}

Then I can do the following: (code will run in LinqPad)

MyInt myInt = new MyInt();
MyInterfacedInt myIInt = new MyInterfacedInt();

(myInt is MyInt).Dump();//true
(myIInt is MyInterfacedInt).Dump();//true;

(myInt is MyGenericClass<int>).Dump();//true
(myIInt is IMyGenericInterface<int>).Dump();//true;

But I cannot do the following:

//Will not compile
//(myInt is MyGenericClass<>).Dump();
//(myInt is IMyGenericInterface<>).Dump();

Why is this important? If you plan on loading plugins at run time, then all you know is that they will implement an interface or a base class and you need to check that. One way around this would be to create a non-generic base class or interface and check against that. But that might not be a possibility if you are using a 3rd party API (eg: Prism and the CompositePresentationEvent<> type).

So here is a helper class that can do just that:

static class ReflectionHelper
{
    public static bool IsDerivedOrImplementedFrom<T>(this T objectToCheck, Type parentType) where T : class
    {
        if (objectToCheck == null)
            return false;
        if (parentType.IsInstanceOfType(objectToCheck))
            return true;

        bool checkingInterfaces = parentType.IsInterface;
        Type toCheck = objectToCheck.GetType();
        while (toCheck != null && toCheck != typeof(object)) {
            var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck;
            if (checkingInterfaces)
            {
                bool implementsParentInterface = toCheck.GetInterfaces()
                                            .Any(ci => {
                                                return ci.IsGenericType ? ci.GetGenericTypeDefinition() == parentType :
                                                    ci == parentType;
                                            });
                if (implementsParentInterface)
                    return true;
            }
            else
            {
                if (parentType == cur)
                {
                    return true;
                }
            }
            toCheck = toCheck.BaseType;
        }
        return false;
    }

}

And here is how you call it:

static void Main(string[] args)
        {
            MyInt myInt = new MyInt();
            MyInterfacedInt myIInt = new MyInterfacedInt();

            "Using the \"is\" keyword".Dump();

            (myInt is MyInt).Dump();//true
            (myIInt is MyInterfacedInt).Dump();//true;

            (myInt is MyGenericClass<int>).Dump();//true
            (myIInt is IMyGenericInterface<int>).Dump();//true;

            //Will not compile
            //(myInt is MyGenericClass<>).Dump();
            //(myInt is IMyGenericInterface<>).Dump();

            "Using IsDerivedOrImplementedFrom".Dump();
           
            myInt.IsDerivedOrImplementedFrom(typeof(MyInt)).Dump(); //true
            myIInt.IsDerivedOrImplementedFrom(typeof(MyInterfacedInt)).Dump();//true
            myInt.IsDerivedOrImplementedFrom(typeof(MyGenericClass<int>)).Dump();//true
            myIInt.IsDerivedOrImplementedFrom(typeof(IMyGenericInterface<int>)).Dump();//true

            myInt.IsDerivedOrImplementedFrom(typeof(MyGenericClass<>)).Dump();//true
            myIInt.IsDerivedOrImplementedFrom(typeof(IMyGenericInterface<>)).Dump();//true

            "Negative tests Using IsDerivedOrImplementedFrom".Dump();

            myInt.IsDerivedOrImplementedFrom(typeof(MyInterfacedInt)).Dump(); //false
            myIInt.IsDerivedOrImplementedFrom(typeof(MyInt)).Dump();//false
            myInt.IsDerivedOrImplementedFrom(typeof(MyGenericClass<string>)).Dump();//false
            myIInt.IsDerivedOrImplementedFrom(typeof(IMyGenericInterface<string>)).Dump();//false

            myInt.IsDerivedOrImplementedFrom(typeof(IMyGenericInterface<>)).Dump();//false
            myIInt.IsDerivedOrImplementedFrom(typeof(MyGenericClass<>)).Dump();//false

            Console.ReadLine();
        }

Here is the full code: http://pastebin.com/raw.php?i=Y2nSQc7V

No comments: