Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a constructed generic type without specifying any type parameters

When we have something like this:

interface ISomething<U,V> { ... }
class Something<U,V> : ISomething<U,V> { ... }

typeof(ISomething<,>) and typeof(Something<,>) will result in a "Generic type definition". But if we get to the interface type as the interface implemented by the class, it will be a constructed type, that none of its type parameters are actually bound:

typeof(Something<,>).GetInterfaces().SingleOrDefault()

MSDN specifically mentions this. What I want, is to construct the same type (constructed type) of ISomething<,> directly (without sub-classing and them looking for the base type), and I could not find any way to do so.

Additional info:

I even tried this:

Type t1 = typeof(ISomething<,>);
Type t2 = t1.MakeGenericType(t1.GetGenericArguments()) // Yields a generic type definition

Type t3 = typeof(Something<,>).GetInterfaces().SingleOrDefault();

In the above code:

t1.Equals(t2) is true, but t1.Equals(t3) is false, obviously because t3 is constructed.

Surprisingly, t1.GetGenericArguments()[0].Equals(t3.GetGenericArguments()[0]) is false, although both are open (IsGenericParameter = true), and I couldn't find any difference in their properties.

And here's why I need to do this: I need a canonical form of storing Type objects in a list. The objects sometimes come from base classes / interfaces (such as t3 above) and sometimes directly (such as t1). I will need to be able to compare these to each other. I can't store the generic type definition (using .GetGenericTypeDefinition()) because sometimes I will have a partially open constructed generic type (like ISomething), and GetGenericTypeDefinition will give me a type without any type arguments specified.

The only way for making the types canonical that I've thought might work, is to check if all of the type arguments are unbound, and do a GetGenericTypeDefinition. Otherwise keep the constructed type.

like image 239
Iravanchi Avatar asked Jul 15 '11 17:07

Iravanchi


People also ask

How do you make a generic type?

To construct a generic type from the generic type definition for a nested type, call the MakeGenericType method with the array formed by concatenating the type argument arrays of all the enclosing types, beginning with the outermost generic type, and ending with the type argument array of the nested type itself, if it ...

How do you indicate that a class has a generic type parameter?

A generic type is declared by specifying a type parameter in an angle brackets after a type name, e.g. TypeName<T> where T is a type parameter.

How do you provide a generic parameterized type?

In order to use a generic type we must provide one type argument per type parameter that was declared for the generic type. The type argument list is a comma separated list that is delimited by angle brackets and follows the type name. The result is a so-called parameterized type.

How do you declare a generic type in Java?

To update the Box class to use generics, you create a generic type declaration by changing the code "public class Box" to "public class Box<T>". This introduces the type variable, T, that can be used anywhere inside the class. As you can see, all occurrences of Object are replaced by T.


1 Answers

You are getting yourself all messed up here. Examine the output of this program and make sure that you understand it. Here I've alpha-renamed the type parameters so that there is no unclarity due to two things both named U:

interface I<S, T>
{
    I<S, T> M();
}

class C<U, V> : I<U, V>
{
    public I<U, V> M() {return null;}
    public C<U, V> N() {return null;}
}

public class MainClass
{
    public static void Main()
    {
        var i1 = typeof(I<,>);
        var i2 = typeof(I<int, int>);
        var i3 = i2.GetGenericTypeDefinition();
        var i4 = i1.GetMethod("M").ReturnType;

        var c1 = typeof(C<,>);
        var c2 = typeof(C<int, int>);
        var c3 = c2.GetGenericTypeDefinition();
        var c4 = c1.GetMethod("N").ReturnType;

        var i5 = c1.GetMethod("M").ReturnType;
        var i6 = c1.GetInterfaces()[0];

        System.Console.WriteLine(i1 == i2); // false -- I<,> is not I<int, int>
        System.Console.WriteLine(i1 == i3); // true  -- I<int,int>'s decl is I<,>
        System.Console.WriteLine(i1 == i4); // true  -- I<,> is I<S, T>
        System.Console.WriteLine(i1 == i5); // false -- I<S, T> is not I<U, V>
        System.Console.WriteLine(i1 == i6); // false -- I<S, T> is not I<U, V>

        System.Console.WriteLine(c1 == c2); // false -- C<,> is not C<int, int>
        System.Console.WriteLine(c1 == c3); // true  -- C<int,int>'s decl is C<,>
        System.Console.WriteLine(c1 == c4); // true  -- C<,> is C<U,V>
    }
}
like image 141
Eric Lippert Avatar answered Oct 01 '22 09:10

Eric Lippert