2015-08-19 3 views
17

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

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 на нем снова открывает его. Почему это?

+3

Класс 'System.Type' не имеет метод с именем' GetGenericTypeArguments'. Вы имели в виду 'GetGenericArguments'? – Wazner

+0

@ Wazner: yup, исправлено. –

+0

https://dotnetfiddle.net/n6jJ4A - Я получаю 0 и 1. –

ответ

17

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

Потому что это один аргумент типа при условии, - параметр типа для B.

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

public class B<T> : A<T> 

Что в A<T>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 
    } 
} 
+0

Точно. Единственное, что я могу добавить, это повторить определение родового типа (что также неявно демонстрирует, как это работает, потому что это работает :-)). Например: typeof (B <>). BaseType.GetGenericTypeDefinition(). GenericTypeArguments.Length' снова даст вам 0. – atlaste

0

Свойство 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<>.

+0

Да, извините, но сначала я использовал неправильный метод в своем примере. Пожалуйста, попробуйте текущий. –

+0

Также обратите внимание, что 'MetadataToken' не является хорошим идентификатором при работе с generics, так как все' A ',' A <> 'и, например, 'A ' будет иметь такое же значение. Конкретные общие реализации выполняются во время выполнения из одного и того же объекта IL (таким образом, он имеет одинаковый токен). –

Смежные вопросы