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

Advertisement

8 Comments »

  1. You seem to be going to an awful lot of work (particularly at run-time) there to find out if an object implements a particular interface. It would seem there are simpler ways (even compile-time ways)

    Given these functions:

    bool IsList(IList list)
    {
    return true;
    }

    bool IsList(object list)
    {
    return false;
    }
    bool IsList(object list)
    {
    return false;
    }

    This code produces the correct results:

    MyList list = new MyList(new int[] { 1, 4, 5 });
    // Is it an IList ? — true
    Console.WriteLine(IsList(list).ToString());
    // Is it any kind of IList ? — true
    Console.WriteLine(IsList(list).ToString());
    // Is it an IList ? — false
    Console.WriteLine(IsList(list).ToString());
    // is an int an IList? — false
    Console.WriteLine(IsList(1).ToString());

    If you need to know what kind of Ilist it is, that can be done too:

    Type IsList(IList list)
    {
    return typeof(T);
    }

    Type IsList(object list)
    {
    return null;
    }

    Type IsList(object list)
    {
    return null;
    }

    Comment by James Curran — February 16, 2007 @ 8:25 pm

  2. booger — you’re editor strips things which look like HTML:
    That should be:
    bool IsList<T>(IList<T> list)
    {
    return true;
    }

    bool IsList<T>(object list)
    {
    return false;
    }

    bool IsList(object list)
    {
    return false;
    }

    and
    Console.WriteLine(IsList<int>((list).ToString());
    Console.WriteLine(IsList(list).ToString());
    Console.WriteLine(IsList<string>((list).ToString());
    Console.WriteLine(IsList(1).ToString());

    Comment by James Curran — February 16, 2007 @ 8:28 pm

  3. I guess you did not get the point; perhaps you should re-read the first paragraph.
    But just in case: the point was to check whether the collection implements IList<T> for any T, i.e. without actually providing T. Your version is just a way to work around the usage of the “is” keword.

    Comment by ajdotnet — February 16, 2007 @ 9:47 pm

  4. I guess you did not get MY point; perhaps you should re-read MY CODE.

    You’ll note in the line:

    Console.WriteLine(IsList(list).ToString());

    I do not specify the type. It returns true if list is any kind of IList<T>

    If you need to find out the type, it can be changed to

    Type IsList<T>(IList<T> list)
    {
    return typeof(T);
    }

    (with those now returing false, chaged to return null)

    Comment by James Curran — February 19, 2007 @ 7:30 pm

  5. Sorry if my last comment was misleading, but this is still not working for me.
    Your code works because “list” is a typed variable. That’s why “IsList(list)” calls the “IsList<T>(IList<T> list)” overload (provided “MyList” implements that interface).

    But try this:
    object o= list;
    if (!IsList(o))
    throw new Exception(“Gee, I would not have expected this!”);

    Guess what? The overload “IsList(object list)” is called and the exception is thrown. In order to make this work I need to cast “o” to a strongly typed list – which is simply not feasible for the kind of generic code I assumed in my post. It would restrict collections to those of a certain type – in which case the whole problem boils down to the usage of “is” anyway.
    In other words: your approach relies on compile time typing, mine on runtime reflection.

    But anyway, until I checked your comment, I would not have expected “IsList<T>(IList<T> list)” to be called unless I wrote something like “IsList<int>(list)”. Thanks for that.

    Comment by ajdotnet — February 20, 2007 @ 10:35 am

  6. What you say is true, but only because you are looking at the problem to narrowly. IF we were to use the IsList functions I described in a “real-world” example, we’d get something which would boil down to this:

    void WhopAnObject(object obj)
    {
    if (IsList(obj))
    WhopAList(onj);
    else
    WhopASingleObject(obj);

    DoCommonWhopping(obj);
    }

    And that would, as you said, fail — but only because we are again doing at run-time what should be done compile time. THe SOlution? Move the test up a level:

    void WhopAnObject(IList list)
    {
    WhopAList(list);
    DoCommonWhopping(list);
    }

    void WhopAnObject(object obj)
    {
    WhopASingleObject(obj)
    DoCommonWhopping(obj);
    }

    Eventually, you reach a level where the function is called where the static type is the same as the dymanic type. The technique does lead to more functions being defined, but they will individually be simpler, so overall this would lead to a better, more factored design.

    Comment by James Curran — February 21, 2007 @ 6:42 pm

  7. Ooops… Forgot about the HTML tags again…

    void WhopAnObject(IList list)

    should be

    void WhopAnObject<T>(IList<T> list)

    Comment by James Curran — February 21, 2007 @ 6:44 pm

  8. Thanks for your post AJ.

    James, you’re missing his point. Sometimes these things can’t be done at compile time, period.

    For instance, imaging you’re dealing with a .NET Remoting server, implemented by a third party. All you know is that server can return you an object which implements the below interface:

    interface IRemoteServer
    {
    object GetWhoppable();
    }

    You’re writing code which has to work with all past, present and future versions of third party servers which implement the above interface. So you can’t just Reflector them all to find out what types they contain – your code has to work on RUNTIME type information, not compile time.

    Now re-read AJ’s post.

    Comment by AttentiveReader — October 1, 2007 @ 2:43 pm


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: