Что такое "специальный класс"?

Не удалось получить что-то вроде следующего для компиляции:

public class Gen<T> where T : System.Array
{
}

с ошибкой

Ограничением не может быть специальный класс `System.Array '

Я начал задаваться вопросом, что такое "специальный класс"?

Люди часто, похоже, получают такую ​​же ошибку, когда они указывают System.Enum в общем ограничении. Я получил те же результаты с System.Object, System.Delegate, System.MulticastDelegate и System.ValueType тоже.

Их больше? Я не могу найти информацию о "специальных классах" на С#.

Кроме того, что особенно важно в отношении тех классов, которые мы не можем использовать в качестве ограничения общего типа?

Ответ 1

Из исходного кода Roslyn он выглядит как список жестко запрограммированных типов:

switch (type.SpecialType)
{
    case SpecialType.System_Object:
    case SpecialType.System_ValueType:
    case SpecialType.System_Enum:
    case SpecialType.System_Delegate:
    case SpecialType.System_MulticastDelegate:
    case SpecialType.System_Array:
        // "Constraint cannot be special class '{0}'"
        Error(diagnostics, ErrorCode.ERR_SpecialTypeAsBound, syntax, type);
        return false;
}

Источник: Binder_Constraints.cs IsValidConstraintType
Я нашел его с помощью поиска GitHub: Ограничение не может быть специальным классом

Ответ 2

Я нашел комментарий Jon Skeet от 2008 года по аналогичному вопросу: Почему поддерживается ограничение System.Enum не.

Я знаю, что это немного не по теме, но он спросил Эрика Липперта (команду С#) об этом, и они предоставили этот ответ:

Во-первых, ваша гипотеза верна; ограничения на ограничения являются по большому счету артефактами языка, а не только CLR. (Мы мы, чтобы сделать эти функции, было бы несколько незначительных вещей, которые мы хотели бы изменение CLR в отношении того, как перечислены типы перечислений, но в основном это была бы работа на языке.)

Во-вторых, я лично хотел бы, чтобы были делегированные ограничения, перечисление ограничения и возможность указывать ограничения, которые являются незаконными сегодня, потому что компилятор пытается спасти вас от себя. (Что, делает закрытые типы законными в качестве ограничений и т.д.)

Однако из-за ограничений по планированию мы, скорее всего, не сможем вставьте эти функции в следующую версию языка.

Ответ 3

В соответствии с MSDN это статический список классов:

Ошибка компилятора CS0702

Ограничение не может быть специальным идентификатором класса. Следующие типы не могут использоваться в качестве ограничений:

  • System.Object
  • System.Array
  • System.Delegate
  • System.Enum
  • System.ValueType.

Ответ 4

В соответствии со спецификацией языка С# 4.0 (Coded: [10.1.5] Ограничения параметра типа) указывает две вещи:

1] Тип не должен быть объектом. Поскольку все типы происходят от объекта, такое ограничение не будет иметь никакого эффекта, если бы оно было разрешено.

2] Если T не имеет первичных ограничений или ограничений параметров типа, его эффективный базовый класс - это объект.

Когда вы определяете общий класс, вы можете применять ограничения на типы типов, которые код клиента может использовать для аргументов типа, когда он создает экземпляр вашего класса. Если код клиента пытается создать экземпляр вашего класса, используя тип, который не допускается ограничением, результатом является ошибка времени компиляции. Эти ограничения называются ограничениями. Ограничения задаются с использованием контекстного ключевого слова where. Если вы хотите ограничить тип общего типа ссылочным типом, используйте: class.

public class Gen<T> where T : class
{
}

Это запретит тип generic быть типом значения, таким как int или struct и т.д.

Кроме того, Constraint не может быть специальным идентификатором класса. Следующие типы не могут использоваться в качестве ограничений:

  • System.Object
  • System.Array
  • System.Delegate
  • System.Enum
  • System.ValueType.

Ответ 5

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

Если, например, было разрешено писать: void CopyArray<T>(T dest, T source, int start, int count); можно было бы передать dest и source методам, которые ожидают аргумент типа System.Array; Кроме того, можно было бы получить проверку времени компиляции, чтобы dest и source были совместимыми типами массивов, но никто не смог бы получить доступ к элементам массива с помощью оператора [].

Невозможность использовать Array в качестве ограничения в основном довольно легко работать, так как void CopyArray<T>(T[] dest, T[] source, int start, int count) будет работать практически во всех ситуациях, где будет работать прежний метод. Однако он имеет слабость: первый метод будет работать в сценарии, когда один или оба аргумента имеют тип System.Array, в то же время отвергая случаи, когда аргументы являются несовместимыми типами массивов; добавив перегрузку, где оба аргумента имели тип System.Array, заставили бы код принять дополнительные случаи, которые он должен принять, но также сделать ошибочно принимать случаи, которые он не должен делать.

Я нахожу решение отказаться от большинства особых ограничений. Единственный, который имел бы нулевое семантическое значение, был бы System.Object [так как если бы это было законным как ограничение, все бы удовлетворяло его). System.ValueType, вероятно, было бы не очень полезно, так как ссылки типа ValueType на самом деле не имеют много общего со значениями типов, но, вероятно, это может иметь некоторое значение в случаях, связанных с Reflection. Оба System.Enum и System.Delegate будут иметь какое-то реальное применение, но поскольку создатели С# не думали о них, они объявлены вне закона по уважительной причине.

Ответ 6

В CLR через С# 4th Edition можно найти следующее:

Первичные ограничения

Параметр типа может указывать нулевые первичные ограничения или одно основное ограничение. Первичный Ограничение может быть ссылочным типом, который идентифицирует класс, который не запечатан. Вы не можете указать один следующих специальных ссылочных типов: System.Object, System.Array, System.Delegate, System.MulticastDelegate, System.ValueType, System.Enum или System.Void. При указании ограничения ссылочного типа вы обещаете компилятору, что указанный тип аргумент будет либо одного типа, либо типа, полученного из типа ограничения.

Ответ 7

Я не думаю, что существует какое-либо официальное определение "специальных классов" / "специальных типов".

Вы можете думать о них как о типах, которые нельзя использовать с семантикой "обычных" типов:

  • вы не можете создавать их напрямую;
  • вы не можете наследовать собственный тип из них напрямую;
  • существует некоторая магия компилятора для работы с ними (необязательно);
  • прямое использование их экземпляров по крайней мере бесполезно (необязательно, представьте, что вы создали общий код выше, какой общий код вы собираетесь писать?)

P.S. Я бы добавил System.Void в список.