4

Let's say I have such a generic class

public class XClass<T, U>
{
    public void MethodA<V>(){}
}

How could I get the type of

XClass<int,>

not hard-coded, not limiting to MakeGenericType method as below.

------ detailed elaboration below using MakeGenericType ------

I can get the type of the unbound and open class "XClass<,>" and its open method:

var type = typeof(XClass<,>);
    Console.WriteLine(String.Format("Type ZClass<,>: \t generic? {0} \t open? {1}"
            , type.IsGenericType, type.IsGenericTypeDefinition));
var method = type.GetMethod("MethodA");
    Console.WriteLine(String.Format("Method MethodA<>: \t generic? {0} \t open? {1}"
            , method.IsGenericMethod, method.IsGenericMethodDefinition));

Also, I can get the type of full closed class

XClass <int, char>

and its close method:

var type = typeof(XClass<,>); 
var method = type.GetMethod("MethodA"); 
var fullType = method.DeclaringType.MakeGenericType(new[]{typeof(int), typeof(char)});
    Console.WriteLine(String.Format("Type ZClass<int,char>: \t generic? {0} \t open? {1}"
            , fullType.IsGenericType, fullType.IsGenericTypeDefinition));
var fullTypeOpenMethod = fullType.GetMethod("MethodA");
var fullMethod = fullTypeOpenMethod.MakeGenericMethod(typeof(string));
    Console.WriteLine(String.Format("Method MethodA<String>:\t generic? {0} \t open? {1}"
            , fullMethod.IsGenericMethod, fullMethod.IsGenericMethodDefinition));

Now, How can I get the type of bound but open class

XClass<int, >

and its method?

var type = typeof(XClass<,>); 
var method = type.GetMethod("MethodA");
Type [] types = new Type[2];
types[0] = typeof(int);
types[1] = null; // what shall i put here?
var halffullType = method.DeclaringType.MakeGenericType(types);

If I put types[1] as null, an ArgumentNullException exception will throw "Value cannot be null".

What should I do?

athos
  • 6,120
  • 5
  • 51
  • 95
  • Are you sure this is possible? The documentation says *"Types constructed with MakeGenericType can be open, that is, some of their type arguments can be type parameters of **enclosing** generic methods or types."* This makes me believe that since your class is not itself enclosed in a generic type, the question is meaningless. – Jon Jul 15 '11 at 09:02
  • yeah Jon, I think I didn't put the question clearly.. sorry about that. i updated the question to say that, not necesarily by MakeGenericType, i'm looking for any way that not hard-coding "int" in. – athos Jul 15 '11 at 09:08
  • I expanded the comment into an answer. – Jon Jul 15 '11 at 09:37

4 Answers4

4

What you are proposing to do is impossible and also will not really help you.

Impossible because...

The documentation states (emphasis mine) that

Types constructed with MakeGenericType can be open, that is, some of their type arguments can be type parameters of enclosing generic methods or types.

This means that you cannot make a Type object representing XClass<int,>. What you can do is:

class Outer<TOuter>
{
    class XClass<T, U> {}
}

In this situation, you can make a Type object representing Outer<TOuter>.XClass<int, TOuter>. But there needs to be an enclosing generic class.

Not useful because...

The documentation also states (referring to a similar example to the above) that:

A constructed type such as Base is useful when emitting code, but you cannot call the MakeGenericType method on this type because it is not a generic type definition. To create a closed constructed type that can be instantiated, first call the GetGenericTypeDefinition method to get a Type object representing the generic type definition and then call MakeGenericType with the desired type arguments.

Which means that if you have

Type myType = ... // represents Outer<TOuter>.XClass<int, TOuter>

Then to get a Type for XClass<int, string> you would first need to call myType.GetGenericTypeDefinition() (thus losing the int information) and then call MakeGenericType to put it back in (along with the string type parameter). So it's like one step back and two steps forward.

Alternatives

You might want to consider storing the type parameter types for XClass in a separate data structure (e.g. a Type[]) for as long as not all type parameters are known to you, and then create the closed generic type in one go after you have collected all of them.

You can also package all this into a small helper class for convenience:

class GenericTypeDescription
{
    private readonly Type openGenericType;
    private readonly Type[] typeParameters;

    public GenericTypeDescription(Type openGenericType)
    {
        // add checks for openGenericType actually being what it says here
        this.openGenericType = openGenericType;
        this.typeParameters = new Type[openGenericType.GetGenericArguments().Length];
    }

    public void SetTypeParameter(int index, Type type) {
        // add error handling to taste
        this.typeParameters[index] = type;
    }

    public Type ConstructGenericType() {
        // add error handling to taste
        return this.openGenericType.MakeGenericType(this.typeParameters);
    }
}
Jon
  • 428,835
  • 81
  • 738
  • 806
2

No, this isn't possible.

See my similar question: Does .Net support curried generics?

Community
  • 1
  • 1
configurator
  • 40,828
  • 14
  • 81
  • 115
1

This is possible, when you feed MakeGenericType with its own generic arguments.

var type = typeof(XClass<,>); 
var method = type.GetMethod("MethodA");
Type[] types = new Type[2];
types[0] = typeof(int);
types[1] = type.GetGenericArguments()[1]; // Use the open parameter type
var openConstructedType = type.MakeGenericType(types);

This will populate openConstructedType with a Type of XClass<int,U>.

Note that the type will have ContainsGenericParameters, so it won't be constructable, and there's no way to populate the open parameters.

  • no longer playing with C#, couldn't test it. but sounds promising! – athos May 08 '18 at 07:28
  • It works in a fairly old version of LINQPad. Everyone seems to interpret the "some of their type arguments can be type parameters of enclosing generic methods or types" part to mean derived types, probably because that's the example MSDN uses, but forget that a type can be its _own_ enclosing type. That's how methods like `MethodA` work: the `V` type is defined by the method it belongs to. Similarly, we can use the `U` type that is defined by `XClass`. – Daniel Chandler May 09 '18 at 02:52
0

I don't think that this is possible without inheriting from the class.

What you seem to be trying is to basically do this via reflection:

typeof(XClass<int,>)

This would be half-closed... and only possible by inheritance AFAIK:

class XClassInt<U>: XClass<int, U> {}

This second code allows you to get typeof(XClassInt<>).BaseType which is kind of what you want. However, in that case the second type argument for XClass<,> is not null but actually U (the type argument coming from XClassInt<>).

See also this MSDN page.

Edit: Here's my testbed for this:

public class C1<A,B> {}

public class C2<B>: C1<int, B> {}

[...]
Type baseType = typeof(C2<>).BaseType;
WL(baseType);
WL(baseType.GetGenericArguments()[0]);
Type arg1 = baseType.GetGenericArguments()[1];
WL(arg1);
WL(arg1.DeclaringType);
WL(arg1.GenericParameterPosition);
WL(arg1.IsGenericParameter);

Running this yields:

C1`2[System.Int32,B]
System.Int32
B
C2`1[B]
0
True

However, as I said, I believe this is only possible because the base type is closed with the generic type argument of the open generic type C2.

Lucero
  • 59,176
  • 9
  • 122
  • 152
  • Actually, that's a closed type where `XClass.U` is assigned to be equal to `XClassInt.U`. Very confusing. – configurator Jul 15 '11 at 09:06
  • yeah, sorry about my long-winded question. Yes I'm looking for get typeof(XClass) without hard-coding... – athos Jul 15 '11 at 09:09
  • Curious... how would CLR translate this class XClassInt: XClass {} ? there must be some intermediate thing for XClass from XClass to XClassInt? – athos Jul 15 '11 at 09:12