Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# base class of unbound generic type not usable?

Tags:

c#

generics

I am stumped by weird behavior while working with generics in C#. Consider this code:

class CEmployee<T> { }
class CManager<T> : CEmployee<T> { }

class Program
{
    static void Main(string[] args)
    {
        CManager<String> cemp = new CManager<String>();
        Console.WriteLine(cemp.GetType());
        Console.WriteLine(cemp.GetType().FullName);
        Console.WriteLine(cemp.GetType().BaseType);
        Console.WriteLine(cemp.GetType().BaseType.FullName);
        Console.WriteLine(1);
        Console.WriteLine(cemp.GetType().GetGenericTypeDefinition());
        Console.WriteLine(cemp.GetType().GetGenericTypeDefinition().FullName);
        Console.WriteLine(cemp.GetType().GetGenericTypeDefinition().BaseType);
        Console.WriteLine(cemp.GetType().GetGenericTypeDefinition().BaseType.FullName); // Problem 1
        Console.WriteLine(2);
        Console.WriteLine(typeof(CManager<>));
        Console.WriteLine(typeof(CManager<>).FullName);
        Console.WriteLine(typeof(CManager<>).BaseType);
        Console.WriteLine(typeof(CManager<>).BaseType.FullName); // Problem 1
        Console.WriteLine(3);
        Console.WriteLine(typeof(CManager<>).Equals(cemp.GetType().GetGenericTypeDefinition()));
        Console.WriteLine(typeof(CEmployee<>).Equals(cemp.GetType().GetGenericTypeDefinition().BaseType)); // Problem 2


    }
}

The output is

ConsoleApplication1.CManager`1[System.String]
ConsoleApplication1.CManager`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutr
ConsoleApplication1.CEmployee`1[System.String]
ConsoleApplication1.CEmployee`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neut
1
ConsoleApplication1.CManager`1[T]
ConsoleApplication1.CManager`1
ConsoleApplication1.CEmployee`1[T]

2
ConsoleApplication1.CManager`1[T]
ConsoleApplication1.CManager`1
ConsoleApplication1.CEmployee`1[T]

3
True
False

I am stumped as to why the base class of the unbound generic type is not fully usable:

  1. Why am I getting nothing when I get the full name of the base type of unbound generic type? ("problem 1" in the code above)
  2. Why if the base type of my CManager instance is CEmployee, and the type of the base class of the unbound type CManager<> is CEmployee<>, and the unbound type CManager "Equals" the generic type definition of my instance, the base type of the unbound type does not "Equals" the base type of the generic type definition of my instance?? ("problem 2" in the code above)
like image 692
Mishax Avatar asked Dec 30 '25 13:12

Mishax


1 Answers

In reverse order:

2: because the base type is CEmployee<T>, not CEmployee<>. There is a difference; CEmployee<> is the type-definition; CEmployee<T> is bound to the T from the parent type. You can see this by looking at IsGenericTypeDefinition and GetGenericArguments()[0]

1: because the FullName of this form of generic type is not defined. This is fully documented:

If the current type contains generic type parameters that have not been replaced by specific types (that is, the ContainsGenericParameters property returns true), but the type is not a generic type definition (that is, the IsGenericTypeDefinition property returns false), this property returns null.

which is exactly what we discussed in "2".

like image 177
Marc Gravell Avatar answered Jan 02 '26 03:01

Marc Gravell