Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic strange behaviour

Tags:

c#

I met with the strange behavior of the generic. Below is the code I use for testing.

public static class Program
{
    public static void Main()
    {
        Type listClassType = typeof(List<int>).GetGenericTypeDefinition();
        Type listInterfaceType = listClassType.GetInterfaces()[0];

        Console.WriteLine(listClassType.GetGenericArguments()[0].DeclaringType);
        Console.WriteLine(listInterfaceType.GetGenericArguments()[0].DeclaringType);
    }
}

Output:

System.Collections.Generic.List`1[T]
System.Collections.Generic.List`1[T]

I found it very strange that the second Console.WriteLine call displays a class, not an interface, because I use a generic type definition. Is this correct behaviour?

I'm trying to implement generic type inference in my compiler. Suppose I have the code below.

public static class GenericClass
{
    public static void GenericMethod<TMethodParam>(IList<TMethodParam> list) { }
}

And I want to call this method as follows:

GenericClass.GenericMethod(new List<int>());

In order to check the possibility of inference, I have to compare the type in the method signature, and type of arguments passed. But the code below returns false.

typeof(GenericClass).GetMethods()[0].GetParameters()[0].ParameterType == listInterfaceType;

Should I always use Type.GetGenericTypeDefinition for such comparisons?

like image 710
Tenere Avatar asked Mar 26 '13 16:03

Tenere


1 Answers

Adding a second answer because you added a second question:

I'm trying to implement generic type inference in my compiler...

And thus I assume you're using reflection to build a compiler. This might not be a good idea. Reflection is much more performant now than it was back in the early days but it is still heavyweight compared to working directly with tokens. And reflection emit cannot emit every possible topology of types; it gets messed up in some scenarios involving nested struct types.

I would consider using CCI instead. We used a modified version of CCI for Roslyn.

the code below returns false.

typeof(GenericClass).GetMethods()[0].GetParameters()[0].ParameterType == listInterfaceType

That's correct. The parameter type is IList<TMethodParam> and listInterfaceType is IList<T> where the T is the generic parameter type declared by List<T>, not the generic parameter type declared by IList<T>. Those are all different types.

Should I always use Type.GetGenericTypeDefinition for such comparisons?

If you want to see if two generic types are both constructions of the same generic type, yes. If that's not what you want to check, then no.

This type system is complicated, so be very careful.

This is another reason to go with a token-based approach rather than a reflection-type-object based approach. When you have tokens in hand it is much easier to distinguish between a TypeDef and a TypeRef.

like image 183
Eric Lippert Avatar answered Nov 08 '22 16:11

Eric Lippert