C #: следует ли считать вложенный класс в универсальном классе универсальным? - PullRequest
10 голосов
/ 28 января 2009
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: Правда

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

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

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

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

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

- EDIT -

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

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

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

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

Ответы [ 4 ]

19 голосов
/ 28 января 2009

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

Ссылка ECMA - §25.1:

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

6 голосов
/ 28 января 2009

Да, ваш вложенный класс абсолютно универсален, потому что 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() производит различный вывод в зависимости от того, как был закрыт универсальный тип - так что да, внутренний класс определенно демонстрирует универсальное поведение.

2 голосов
/ 28 января 2009

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

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

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

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

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

2 голосов
/ 28 января 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...