AJ’s blog

August 1, 2006

Is GetInterface() broken?

Filed under: .NET, .NET Framework, C# — ajdotnet @ 6:41 pm

A previous post raised an interesting follow-up question. Suppose you wrote some fairly generic piece of code, like a serialization engine or … hey, why not an objectmapper? (Back to Gerhard, www.objectmapper.net ;-) ) In this case you would not only want to check whether a collection implements, say IList<int>, or IList<Customer>. You would want to support any kind of collection, therefore you would want to know whether it implements any IList<> derived interface. Right?

So, let’s do some type checking…

List<int> list = new List<int>(new int[] { 1, 4, 5 });                

bool isListOfInt = (list is List<int>);
bool isListOfT = (list is List<>); // compiler error

OK, the classical c# is/as does not work because the compiler refuses to accept the generic version. So we have to roll our own type check using reflection.

List<int> list = new List<int>(new int[] { 1, 4, 5 });
Type ilistType = typeof(IList<>); // IList<>
Type listType = list.GetType(); // List<int>                

Type listGenericType = listType.GetGenericTypeDefinition(); // List<>
bool isIListOfT = (listGenericType == ilistType);
    // false: List<> vs. IList<>

Note that typeof works fine with the generic IList<> type. But this first approach was a little naive as it compares the wrong generics: The interface generic IList<> and the list implementation generic List<>. Yet we are looking for the interface rather than a “concrete” list implementation. So we have to ask for an interface, yet, again, we do not know which interface to ask for, do we?
It turns out that there is a way to ask for a interface that is based on a generic without actually telling what the generic type parameters are: It’s documented in a note in Type.GetInterface(string) (see http://msdn2.microsoft.com/en-us/library/ayfa0fcd.aspx) and it works by passing the name of the generic and the number of type parameters:

List<int> list = new List<int>(new int[] { 1, 4, 5 });
Type ilistType = typeof(IList<>); // IList<>
Type listType = list.GetType(); // List<int>                

Type ilistOfX = listType.GetInterface("IList`1"); // IList<int>
Type ilistGenericType = ilistOfX.GetGenericTypeDefinition(); // IList<>
bool isIListOfT2 = (ilistGenericType == ilistType);
    // true: IList<> vs. IList<>

Finally we’re there. Wait… . The generic name. And the number of parameters…. . What if some genius wrote a class that implemented two incarnations of that generic interface…? Let’s see.

Just for the curious, academic pea counters, we’ll start with simply deriving the list class and see what happens to the code above:

public class MyList : List<int>
{
    public MyList(int[] args)
        : base(args)
    {
    }
}                

MyList list = new MyList(new int[] { 1, 4, 5 });
Type ilistType = typeof(IList<>); // IList<>                

Type listType = list.GetType();     // MyList
Type listGenericType = listType.GetGenericTypeDefinition();
    // System.Exception {System.InvalidOperationException:
    // "Operation is not valid due to the current state of the object."

I know, this is based on the first snippet that didn’t work in the first place, so why should it now. But isn’t it peculiar how some innocent change causes quite a different error? Raising an exception rather than simply returning the wrong result?
Anyway, back to the working stuff: To make a long story short the variation above still works (code ommited). Now let’s change the class definition again:

public class MyList : List<int>, IList<double>
{
    public MyList(int[] args)
        : base(args)
    {
    }                

[...] // implementations omitted
}                

MyList list = new MyList(new int[] { 1, 4, 5 });
Type ilistType = typeof(IList<>); // IList<>                

Type listType = list.GetType(); // MyList
Type ilistOfX = listType.GetInterface("IList`1");
    // System.Reflection.AmbiguousMatchException

And you thought, we were done. Ts ts ts…

Since there is no GetInterfaces() method that takes a string as filter we’ll have to do everything by hand. Get all interfaces, loop, get generic, check… :

MyList list = new MyList(new int[] { 1, 4, 5 });
Type ilistType = typeof(IList<>); // IList                

Type listType = list.GetType();     // MyList
Type[] interfaces = listType.GetInterfaces();
bool isIListOfT = false;
Type genericType;
foreach (Type type in interfaces)
{
    if (!type.IsGenericType)
        continue;
    genericType = type.GetGenericTypeDefinition();
    if (genericType != ilistType)
        continue;
    isIListOfT = true;
    break;
}

Finally there. Welcome back, Mr. GetInterfaces, I saw you in a previous post, didn’t I? I know a relative of you, one Mr. GetInterface. He’s a little dangerous, don’t you think? I thought we could get along quite easily – but all of a sudden he attacked me from behind and bit me in the back. What do you mean, you knew that would happen? What muzzle? … Yes, usually he behaved quite good… Bad childhood? … no, I didn’t feed him ….

That’s all for now folks,
AJ.NET

Blog at WordPress.com.