С#: Если вложенный класс в родовом классе считается общим?

namespace GenericsTest
{
    public class AGenericClass<T>
    {
        public class NestedNonGenericClass
        {
        }
    }
}

В приведенном выше примере следует NestedNonGenericClass считать общий класс?

API отражения говорит об этом универсальном классе и даже передает мне параметры шаблона содержащего класса в качестве параметров шаблона вложенного класса.

Type nestedClass = typeof(AGenericClass<int>.NestedNonGenericClass);
Console.Out.WriteLine("IsGeneric: {0}\tHasGenericArguments: {1}", 
   nestedClass.IsGenericType, nestedClass.GetGenericArguments().Length > 0);

Это выдает:

IsGeneric: True HasGenericArguments: True

Я не совсем согласен с этим поведением. Даже если компилятор генерирует общий тип для NestedNonGenericClass, я хотел бы знать, является ли это общим, потому что он был объявлен так или потому, что контейнер является общим.

Итак, мой вопрос:

Во-первых, вы считаете, что нормально рассматривать вложенный класс generic, потому что контейнер является общим? Почему/почему?

Во-вторых, знаете ли вы, что какой-то другой API, который может помочь мне идентифицировать только классы, объявленные как общие?

P.S: Я не мог найти ничего связанного с этим в спецификациях ECMA для дженериков (или, возможно, просто замаскировал его).

- EDIT -

Чтобы добавить немного больше контекста, я работаю над генератором кода. И я использую API отражения, чтобы определить, является ли тип общим.

У меня возникла проблема с Dictionary<TKey, TValue>.KeyCollection.

В KeyCollection API отражения говорит, что он общий и передает мне сообщения TKey и TValue, которые были объявлены в контейнере. Таким образом, генератор заканчивает создание Dictionary<TKey, TValue>.KeyCollection<Tkey, TValue>

Единственный способ решить эту проблему - сопоставить параметры шаблона вложенного класса с контейнером и устранить все те, которые соответствуют. Но мне было интересно, есть ли лучший подход.

Ответ 1

Короче говоря, да - тип наследует аргументы типа из любых типов, которые его содержат: это ключ к таким вещам, как List<T>.Enumerator и многие другие сценарии и т.д. - очень важно, чтобы они делили T с внешний класс (не только любой T).

Ссылка ECMA §25.1:

Любой класс, вложенный внутри общего объявление класса или общая структура декларация (§25.2) сама по себе является объявление общего класса, поскольку тип параметры для типа должны быть предоставлены для создания построенный тип.

Ответ 2

Да, ваш вложенный класс абсолютно общий, потому что T привязан к типу (это называется закрытым общим) в рамках любого экземпляра вложенного класса.

using System;
using System.Collections.Generic;

public class AGenericClass<T> {
    public class NestedNonGenericClass {
        public void DoSomething() {
            Console.WriteLine("typeof(T) == " + typeof(T));
        }
    }
}

public class MyClass {
    public static void Main()   {
        var c = new AGenericClass<int>.NestedNonGenericClass();
        var d = new AGenericClass<DateTime>.NestedNonGenericClass();
        c.DoSomething();
        d.DoSomething();
        Console.ReadKey(false); 
    }

}

Тот же метод DoSomething() производит разные выходные данные в зависимости от того, как был закрыт общий тип - так что да, внутренний класс определенно демонстрирует общее поведение.

Ответ 3

Если вы не собираетесь использовать T в NestedNonGenericClass, вы можете просто разместить его вне класса и сделать его приватным.. тогда это не было бы общим...

Ответ 4

То, что я решил понять, это когда вы используете общий тип, С# генерирует все в этом типе. Так что, если бы я мог визуализировать то, что было бы сгенерировано, если бы я использовал AGenericClass и AGenericClass из вашего примера выше, вы получили бы две копии вложенного класса:

public class AGenericClass<int>
{
    public class NestedNonGenericClass
    {
    }
}

public class AGenericClass<float>
{
    public class NestedNonGenericClass
    {
    }
}

Из-за этого я рассмотрел бы вложенный класс как общий класс, так как есть две его версии: один из них - AGenericClass <int> .NestedNonGenericClass, а другой - AGenericClass <float> .NestedNonGenericClass. Так что, действительно, это похоже на то, что вы явно указали, что вложенный класс также является общим. Такое поведение может быть очень полезно, если вы хотите, чтобы вложенный класс адаптировался к родовому типу.

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