Подтвердить что ты не робот

Почему базовый тип открытого родового типа не открывается?

Рассмотрим фрагмент кода ниже:

public class A<T> { }

public class B<T> : A<T> { }

В таком случае:

var a = typeof(A<>).GenericTypeArguments.Length;

a имеет значение 0, что неудивительно. Это, однако, как-то неожиданно для меня:

var b = typeof(B<>).BaseType.GenericTypeArguments.Length;

где b имеет значение 1. Поэтому он закрывается с использованием несуществующего типа имени "T", и только выполнение GetGenericTypeDefinition на нем заставляет его снова открываться. Почему это?

4b9b3361

Ответ 1

Поэтому он закрывается с использованием несуществующего типа имени "T", и только выполнение GetGenericTypeArgument на нем заставляет его снова открываться. Почему это?

Поскольку имеется один аргумент типа - параметр типа B.

Посмотрите, как вы указываете базовый класс:

public class B<T> : A<T>

Что такое T в A<T>, если это не аргумент типа? Просто потому, что аргумент типа сам по себе является параметром типа, не означает, что он не указывается как аргумент типа.

Рассмотрим это:

public class A<T1, T2> { }

public class B<T> : A<T, int> { }

Здесь базовый класс B<T> равен A<T, int> - и вы можете определить, что int был указан, запросив аргументы типа. Вы также можете показать, откуда приходит T:

using System;
using System.Reflection;
using System.Collections.Generic;

public class A<T1, T2> { }

public class B<T> : A<T, int> { }

class Program
{
    static void Main()
    {
        var bT = typeof(B<>).GetTypeInfo().GenericTypeParameters[0];
        var listT = typeof(List<>).GetTypeInfo().GenericTypeParameters[0];
        var bBaseArguments = typeof(B<>).BaseType.GenericTypeArguments;
        Console.WriteLine(bBaseArguments[0] == bT); // True
        // Shows that the T from B<T> isn't the same as the T from List<T>
        Console.WriteLine(bBaseArguments[0] == listT); // False
        Console.WriteLine(bBaseArguments[1] == typeof(int)); // True
    }
}

Ответ 2

Свойство Type.GenericTypeArguments возвращает типы, используемые в качестве общих параметров для закрытого родового типа, для открытых генерических типов возвращает массив emtpy.

Причина typeof(B<>).BaseType.GenericTypeArguments не возвращает пустой массив, потому что это не открытый общий тип.

Чтобы проиллюстрировать это поведение, я использовал Type.MetadataToken для идентификации типов.

Console.WriteLine(typeof(A<>).GetGenericArguments()[0].MetadataToken);
Console.WriteLine(typeof(B<>).GetGenericArguments()[0].MetadataToken);
Console.WriteLine(typeof(B<>).BaseType.GetGenericArguments()[0].MetadataToken);

Это отображает на экране следующее (числа будут меняться, но равенство останется неизменным):

704643073
704643074
704643074

Это указывает на то, что BaseType типа B<> действительно является закрытым типом, имеющим свой общий параметр "виртуальный" общий тип класса A<>.