views:

114

answers:

4

Is there any way to tell via reflection that a generic list of Type A is related to a generic list of Type B? For example, I have a List<string> and a List<int>. How can I tell via reflection that both these types are 'instances' of List<T>. I think I'm having a problem because List<T> isn't a real type. You can't do typeof(List<T>) for example. List<T> is a compiler trick I guess. So is there anyway to determine if two different types come from List<T>?

+5  A: 

Actually List<T> is a real type in many ways (you can use typeof(List<>), for example), and List<T> is not merely a compiler trick, but a runtime trick. But you can indeed check the open generic type, via something like:

    static Type GetRawType(Type type)
    {
        return type.IsGenericType ? type.GetGenericTypeDefinition() : type;
    }
    static void Main()
    {
        List<int> list1 = new List<int>();
        List<string> list2 = new List<string>();
        Type type1 = GetRawType(list1.GetType()),
            type2 = GetRawType(list2.GetType());
        Console.WriteLine(type1 == type2); // true
    }
Marc Gravell
GetGenericTypeDefinition() is always returning a Type of System.RuntimeType. That's not exactly what I'm looking for. I was expecting to get back 'System.List<T>' or something. I'm sure lots of things are System.RuntimeType that have nothing to do with List<T>. Am I just doing something wrong?
BowserKingKoopa
Maybe I should rephrase the question. How can I tell if a given type comes from List<T>' ?
BowserKingKoopa
@Bowser, GetGenericTypeDefinition() is what you want. bool isListOfT = type.IsGenericType
Josh Einstein
It's sort of what I want. I get back a Type object but the .Name property of the Type object is 'System.RuntimeType'. I need something more identifiable than that because I plan on persisting and then recreating these objects. If I do GetGenericTypeDefinition() on a HashSet<T> I also get back a Type called 'System.RuntimeType'. Sure it's a different Type than the one I got back from the List<T>.GetGenericTypeDefinition() but the name alone isn't enough information to know what generic type it really came from.
BowserKingKoopa
No, then you're going one level too deep. typeof(List<>).Name and typeof(List<string>).Name both return "List`1".
Josh Einstein
Oops, you're absolutely right, I was going one level too deep. Thank you.
BowserKingKoopa
@Josh thanks for fielding that; my train went out of signal range ;-p
Marc Gravell
I am relying on train tunnels and London fog to catch up to you and Jon Skeet. LOL
Josh Einstein
+1  A: 

Try:

typeof(List<string>).GetGenericTypeDefinition() == typeof(List<int>).GetGenericTypeDefinition()

(see http://msdn.microsoft.com/en-us/library/system.type.getgenerictypedefinition.aspx)

Rob Fonseca-Ensor
+8  A: 

Sure you can... List<> is actually what's called an "unbound generic type" meaning it has not been parameterized with a type. When the type argument is specified it's called a "bound generic type". A type which involves "raw" type parameters, like List<T> is an "open generic type", and one that involves only actual types, like List<int> is a "closed generic type". The only situation in which an unbound generic type may be used in C# is in the typeof operator. To access the unbound type, or a closed type you would do:

Type listOfT = typeof(List<>); // unbound type
Type listOfString = typeof(List<string>); // closed bound type
Type listOfInt32 = typeof(List<int>); // closed bound type

Assert.IsTrue(listOfString.IsGenericType);

Assert.AreEqual(typeof(string), listOfString.GetGenericTypeParameters()[0]);
Assert.AreEqual(typeof(List<>), listOfString.GetGenericTypeDefinition());

Type setOfString = typeof(HashSet<string>);

Assert.AreNotEqual(typeof(List<>), setOfString.GetGenericTypeDefinition());
Josh Einstein
Ah Ok. I see. That seems to be working. One problem I'm having though is that the Name of the Type object that comes back from List<> is 'System.RuntimeType'. If I do the same thing for HashSet<T> I also get back a Type called 'System.RuntimeType'. They are two different Type objects with the same name. So how can I tell them apart? I need to persist this info somewhere and I have nothing to persist since I don't get back a name like 'System.List<>' that I can understand.
BowserKingKoopa
Yes they are both RuntimeType in the same way that "hello".GetType() and 123.GetType() are both System.Type - but they represent two different types. You can examine the FullName property if you want but if you're trying to test if a particular type is listOfT then you should compare equality as shown above.
Josh Einstein
FullName also returns just 'System.RuntimeType'. Since I can't get any unique name from the Types coming back from System.GetGenericTypeDefinition() I can't persist the fact that the Type came from a List<T>. I'm trying to persist these types using their Names. So two different Types with the same name is screwing me up.
BowserKingKoopa
I apologize to Marc for junking up his answer with comments. I'm jumping all over the place - I'll keep my comments under my own answer from now on. As I mentioned under Marc's answer I think you are going one level too deep. Try Console.WriteLine( typeof(List<>).FullName ) and Console.WriteLine(typeof(List<string>).FullName) and you'll see what I mean.
Josh Einstein
I was printing out the value of the type name incorrectly accidentally. You're right. This is exactly what I need. Thank you.
BowserKingKoopa
Though your point is well taken, your terminology is incorrect. List<> is not an open generic type. List<int> is a *closed* generic type and a *bound* generic type. List<T> is an *open* generic type and a *bound* generic type. List<> is an *unbound* generic type, not an *open* generic type.
Eric Lippert
Probably easiest if I just fix the text, if you don't mind.
Eric Lippert
Sure, by all means.
Josh Einstein
+1  A: 

Call GetGenericTypeDefinition:

    List<string> l1 = new List<string>();
    List<int> l2 = new List<int>();
    Type t1 = l1.GetType().GetGenericTypeDefinition();
    Type t2 = l2.GetType().GetGenericTypeDefinition();
    Console.Write(t1 == t2);//output: true;
Eric Mickelsen