Что именно представляет собой "Специальный класс"?
После неудачной попытки получить что-то вроде следующего для компиляции:
public class Gen<T> where T : System.Array
{
}
с ошибкой
Ограничение не может быть специальным классом System.Array.
Я начал задаваться вопросом, что же такое "особый класс"?
Люди часто, кажется, получают такую же ошибку, когда они указывают System.Enum
в общем ограничении. Я получил те же результаты с System.Object
, System.Delegate
, System.MulticastDelegate
а также System.ValueType
тоже.
Их больше? Я не могу найти информацию о "специальных классах" в C#.
Кроме того, что такого особенного в этих классах, что мы не можем использовать их как ограничение общего типа?
7 ответов
Из исходного кода 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: "Ограничение не может быть специальным классом"
Я нашел комментарий Джона Скита от 2008 года по аналогичному вопросу: почему System.Enum
ограничение не поддерживается.
Я знаю, что это немного не по теме, но он спросил об этом Эрика Липперта (команда C#), и они ответили:
Во-первых, ваша гипотеза верна; ограничения на ограничения являются в целом артефактами языка, а не CLR. (Если бы мы делали эти функции, в CLR мы хотели бы изменить несколько незначительных моментов, касающихся того, как указываются перечислимые типы, но в основном это будет языковая работа.)
Во-вторых, я лично хотел бы иметь ограничения делегирования, ограничения перечисления и возможность задавать ограничения, которые сегодня недопустимы, потому что компилятор пытается спасти вас от себя. (То есть сделать закрытые типы легальными в качестве ограничений и т. Д.)
Однако из-за ограничений по расписанию мы, вероятно, не сможем перенести эти функции в следующую версию языка.
Согласно MSDN это статический список классов:
Ошибка компилятора CS0702
Ограничение не может быть специальным идентификатором класса. Следующие типы не могут использоваться в качестве ограничений:
- System.Object
- System.Array
- System.Delegate
- System.Enum
- System.ValueType.
Согласно Спецификации языка C# 4.0 (Кодированный: [10.1.5] Ограничения параметров типа) говорится две вещи:
1] Тип не должен быть объектом. Поскольку все типы являются производными от объекта, такое ограничение не будет иметь эффекта, если оно будет разрешено.
2] Если T не имеет первичных ограничений или ограничений параметров типа, его эффективным базовым классом является объект.
Когда вы определяете универсальный класс, вы можете применять ограничения к типам типов, которые клиентский код может использовать для аргументов типа, когда он создает экземпляр вашего класса. Если клиентский код пытается создать экземпляр вашего класса, используя тип, который не разрешен ограничением, результатом будет ошибка времени компиляции. Эти ограничения называются ограничениями. Ограничения задаются с помощью контекстного ключевого слова where. Если вы хотите ограничить универсальный тип ссылочным типом, используйте: class.
public class Gen<T> where T : class
{
}
Это запретит универсальному типу быть типом значения, таким как int или struct и т. Д.
Кроме того, ограничение не может быть специальным идентификатором класса. Следующие типы не могут использоваться в качестве ограничений:
- System.Object
- System.Array
- System.Delegate
- System.Enum
- System.ValueType.
В Framework есть определенные классы, которые эффективно передают специальные характеристики всем производным от них типам, но сами не обладают этими характеристиками. Сам CLR не налагает запрета на использование этих классов в качестве ограничений, но ограниченные им универсальные типы не будут приобретать ненаследуемые характеристики, как это делают конкретные типы. Создатели C# решили, что, поскольку такое поведение может запутать некоторых людей, и они не видят в этом никакой пользы, им следует запретить такие ограничения, а не позволять им вести себя так, как в 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
на самом деле не имеет ничего общего с типами значений, но он может иметь определенное значение в случаях, связанных с отражением. И то и другое System.Enum
а также System.Delegate
будет иметь реальное применение, но поскольку создатели C# не думают о них, они объявлены вне закона без веской причины.
Следующее может быть найдено в CLR через C# 4-е издание:
Основные ограничения
Параметр типа может указывать ноль первичных ограничений или одно первичное ограничение. Основным ограничением может быть ссылочный тип, который идентифицирует класс, который не запечатан. Нельзя указать один из следующих специальных ссылочных типов: System.Object, System.Array, System.Delegate,System.MulticastDelegate,System.ValueType, System.Enum или System.Void. При указании ограничения ссылочного типа вы обещаете компилятору, что заданный аргумент типа будет либо того же типа, либо типа, производного от типа ограничения.
Я не думаю, что существует какое-либо официальное определение "специальных классов"/"специальных типов".
Вы можете думать о них как о типах, которые нельзя использовать с семантикой "обычных" типов:
- вы не можете создавать их экземпляры напрямую;
- вы не можете наследовать пользовательский тип от них напрямую;
- есть некоторая магия компилятора для работы с ними (опционально);
- прямое использование их экземпляров, по крайней мере, бесполезно (опционально; представьте, что вы создали обобщенный код выше, какой универсальный код вы собираетесь писать?)
PS я бы добавил System.Void
к списку.