Search code examples
c#genericstypechecking

How do I detect that an object is a generic collection, and what types it contains?


I have a string serialization utility that takes a variable of (almost) any type and converts it into a string. Thus, for example, according to my convention, an integer value of 123 would be serialized as "i:3:123" (i=integer; 3=length of string; 123=value).

The utility handles all primitive type, as well as some non-generic collections, like ArrayLists and Hashtables. The interface is of the form

public static string StringSerialize(object o) {}

and internally I detect what type the object is and serialize it accordingly.

Now I want to upgrade my utility to handle generic collections. The funny thing is, I can't find an appropriate function to detect that the object is a generic collection, and what types it contains - both of which pieces of information I need in order to serialize it correctly. To date I've been using coding of the form

if (o is int) {// do something}

but that doesn't seem to work with generics.

What do you recommend?


EDIT: Thanks to Lucero, I've gotten closer to the answer, but I'm stuck at this little syntactical conundrum here:

if (t.IsGenericType) {
  if (typeof(List<>) == t.GetGenericTypeDefinition()) {
    Type lt = t.GetGenericArguments()[0];
    List<lt> x = (List<lt>)o;
    stringifyList(x);
  }
}

This code doesn't compile, because "lt" is not allowed as the <T> argument of a List<> object. Why not? And what is the correct syntax?


Solution

  • Re your conundrum; I'm assuming stringifyList is a generic method? You would need to invoke it with reflection:

    MethodInfo method = typeof(SomeType).GetMethod("stringifyList")
                .MakeGenericMethod(lt).Invoke({target}, new object[] {o});
    

    where {target} is null for a static method, or this for an instance method on the current instance.

    Further - I wouldn't assume that all collections are a: based on List<T>, b: generic types. The important thing is: do they implement IList<T> for some T?

    Here's a complete example:

    using System;
    using System.Collections.Generic;
    static class Program {
        static Type GetListType(Type type) {
            foreach (Type intType in type.GetInterfaces()) {
                if (intType.IsGenericType
                    && intType.GetGenericTypeDefinition() == typeof(IList<>)) {
                    return intType.GetGenericArguments()[0];
                }
            }
            return null;
        }
        static void Main() {
            object o = new List<int> { 1, 2, 3, 4, 5 };
            Type t = o.GetType();
            Type lt = GetListType(t);
            if (lt != null) {
                typeof(Program).GetMethod("StringifyList")
                    .MakeGenericMethod(lt).Invoke(null,
                    new object[] { o });
            }
        }
        public static void StringifyList<T>(IList<T> list) {
            Console.WriteLine("Working with " + typeof(T).Name);
        }
    }