Список закрытых типов, которые среда выполнения создала из открытых общих типов

Когда я перечисляю все типы в текущем AppDomain, я вижу свои общие типы с типовыми заполнителями. Однако, если я создаю экземпляр общих типов с типом и затем перечисляю все типы в appDomain, я не вижу вновь созданные закрытые типы.

В приведенном ниже примере вывод только:

Foo`1[T]

Я ищу закрытый тип:

Foo`1[System.Int32]

Есть ли способ увидеть закрытые типы, созданные во время выполнения для меня на основе открытых открытых типов?

class Foo<T>
{
}

class Program
{
    static void Main(string[] args)
    {
        var tmp = new Foo<int>();
        ListTypes();
    }

    private static void ListTypes()
    {
        var types = from assembly in AppDomain.CurrentDomain.GetAssemblies()
                        from type in assembly.GetTypes()
                        where type.Name.Contains("Foo")
                        select type;

        foreach (var type in types)
            Console.WriteLine(type.ToString());
    }
}

Я также попытался найти все типы по универсальному аргументу в надежде обнаружить закрытый тип.

class Foo<T>
{
}

class Bar
{
}

class Program
{
    static void Main(string[] args)
    {
        var tmp = new Foo<Bar>();
        ListTypes();
    }

    private static void ListTypes()
    {
        var types = from assembly in AppDomain.CurrentDomain.GetAssemblies()
                        from type in assembly.GetTypes()
                        where type.IsGenericType
                        && type.GetGenericArguments().Contains(typeof(Bar))
                        select type;

        foreach (var type in types)
            Console.WriteLine(type.ToString());
    }
}

Это просто для удовлетворения моего любопытства.

Ответ 1

Насколько я понимаю, в этом случае Foo<T> - открытый несвязанный общий тип, поэтому в среде выполнения CLR будет использовать его в качестве чертежа/скелета для построения и закрытия общего типа с указанным типом типа параметра (Foo<int>, Foo<object> и т.д.). Таким образом, в основном Foo<int> представляет собой встроенную реализацию скелета Foo<T>, выполненную во время выполнения.

Теперь, во время выполнения, вы можете получить тип Foo<int> либо с помощью typeof(Foo<int>), либо typeof(Foo<>).MakeGenericType(new[] { typeof(int) }), и это не то же самое Type, и это не имеет смысла. Но посмотрите ближе, и вы увидите, что оба typeof(Foo<T>) и typeof(Foo<int>) используют один и тот же токен метаданных и GUID.

Еще одна интересная вещь: typeof(Foo<int>).Assembly будет тем, что вы ожидали бы, но, как вы уже заметили, вы не можете получить этот тип из сборки.

Это потому, что Foo<int> не определено в сборке (вы можете проверить метаданные сборки с помощью Reflector/ILSpy). Во время выполнения CLR создаст ( "построит" ) специализированную ( "закрытую" ) версию Foo<T> для Foo<int> (так построенный закрытый тип неограниченного открытого определения общего типа) и "даст" его Type. Поэтому, если CLR не предоставляет непосредственно каким-то образом список закрытых родовых типов, которые он генерирует во время выполнения, вам не повезло.

Также приведен фрагмент, который может подтвердить, что я говорю:

Несмотря на то, что каждая конструкция общего типа, такая как Node Формa > и Node String > , имеет свой собственный идентификатор определенного типа, CLR способен для повторного использования большей части фактического JIT-скомпилированного кода между типом конкретизации. Это значительно снижает раздувание кода и возможно потому что различные экземпляры родового типа расширяются на время выполнения. Все, что существует во встроенном типе во время компиляции, тип ссылка. Когда сборки A и B ссылаются на общий тип определенные в третьей сборке, их построенные типы расширяются при время выполнения. Это означает, что помимо совместного использования типов CLR (при необходимости), типы экземпляров из сборок A и B также совместно использовать ресурсы времени выполнения, такие как собственный код и расширенные метаданные.

http://msdn.microsoft.com/en-us/magazine/cc163683.aspx

Ответ 2

Ответ Ivan в основном прав, но утверждая, что метаданные сборки не содержат никакой информации о построенных типах, не совсем корректны. Все построенные типы определены в сборке, которая их использует, и такие инструменты, как Mono.Cecil, позволяют вам видеть это. Построенные типы не отображаются через отражение и даже Mono.Cecil затрудняет их поиск.

В основном вам нужно пройти через все типы, которые используются в сборке, например. типы свойств, типы возвращаемых данных, типы локальных переменных и т.д. Эта информация содержится в метаданных сборки и достаточно проста в перечислении с помощью Mono.Cecil. Затем просто примените простой фильтр, который определяет, будет ли тип построен. Обратите внимание, что вам, возможно, придется пройти через несколько сборок, которые ссылаются на определение общего типа, чтобы найти все построенные из него типы.

Существует два ограничения для этого решения. Во-первых, типы, построенные с помощью отражения, естественно, не появляются ни в одной сборке. Во-вторых, некоторые построенные типы внедряются в общие типы/методы, и их аргументы общего типа известны только после того, как их родительский тип/метод создается с помощью конкретных типичных аргументов типа.