Що саме "Спеціальний клас"?


114

Після того як не вдалося скласти щось на зразок наступного:

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

з помилкою

Обмеженням не може бути спеціальний клас `System.Array '

Я почав цікавитись, що саме таке «особливий клас»?

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

Чи є їх більше? Я не можу знайти жодної інформації про "спеціальні класи" в C #.

Крім того , що це так особливого тих класів , які ми не можемо використовувати їх в якості універсального типу обмеження?


14
Я не думаю, що це прямий дублікат. Питання не в тому, "чому я не можу використовувати це як обмеження", це "які це спеціальні класи". Я переглянув ці питання, і вони просто констатують, чому було б марно використовувати його як обмеження, не пояснюючи, що насправді "особливий клас" і чому його вважають особливим.
Адам Холдсворт

2
На мій досвід, класи, які використовуються, але ви не можете використовувати їх безпосередньо, лише неявно через інший синтаксис, це спеціальні класи. Енум потрапляє до тієї ж категорії. Що саме робить їх особливими, я не знаю.
Лассе В. Карлсен

@AndyKorneyev: це питання дещо інше. Я прошу визначити "спеціальний клас" та / або вичерпний їх перелік. Це питання просто задає причину System.Array не може бути загальним обмеженням типу.
Мінц97

З документації зазначено, що "[...] тільки система та компілятори можуть виводити явно з класу Array.". Ймовірно, саме це робить його спеціальним класом - до нього звертається спеціально компілятор.
РБ.

1
@RB .: неправильно. Ця логіка означала б, що System.Objectце не "спеціальний клас", як це справедливо:, public class X : System.Object { }але System.Objectвсе ж є "спеціальний клас".
Мінц97

Відповіді:


106

З вихідного коду 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: "Обмеження не може бути спеціальним класом"


1
@kobi 702 стає помилкою компілятора CS0702, як видно з результатів компілятора (який це питання нехтувати цитуванням) та інших відповідей.
AakashM

1
@AakashM - Дякую! Я спробував компілювати і чомусь не отримав номер помилки. Потім мені знадобилося майже 5 хвилин, щоб дізнатися це, і мені не вистачило часу, щоб відредагувати коментар. Сумна історія.
Кобі

1
@Kobi: вам потрібно подивитися на вихідне вікно, там ви знайдете точний номер помилки компілятора CS0702.
Тім Шмелтер

9
Тож тепер справжнє питання: чому саме ці спеціальні класи?
Девід каже, що повернеться до Моніки

@DavidGrinberg Можливо, причина полягає в тому, що ви не можете наслідувати ці типи безпосередньо (крім object), або, принаймні, це щось стосується. Також where T : Arrayдозволить здати аналіз як T, що, мабуть, не те, чого хоче більшість людей.
IllidanS4 хоче, щоб Моніка повернулася

42

Я знайшов коментар Джона Скіта з 2008 року щодо аналогічного питання: Чому System.Enumобмеження не підтримується.

Я знаю, що це трохи поза темою , але він запитав Еріка Ліпперта (команда C #) про це, і вони дали цю відповідь:

По-перше, ваша думка правильна; обмеження обмежень - це загалом артефакти мови, не стільки CLR. (Якби ми зробили ці функції, було б декілька незначних речей, які ми хотіли б змінити в CLR щодо того, як вказані численні типи, але в основному це буде мовна робота.)

По-друге, я особисто хотів би мати делегатські обмеження, обмеження перерахунків та можливість вказувати обмеження, які сьогодні є незаконними, оскільки компілятор намагається врятувати вас від себе. (Тобто, зробити герметичні типи законними як обмеження тощо).

Однак через обмеження планування ми, швидше за все, не зможемо перетворити ці функції в наступну мову мови.


10
@YuvalItzchakov - Чи краще цитування Github \ MSDN? Команда C # дала конкретну відповідь щодо питання чи подібного. Насправді це нікому не зашкодить. Джон Скіт просто цитував їх і є досить надійним, коли він потрапляє на C # ..
Амір Попович

5
Не потрібно засмучуватися. Я не мав на увазі, що це неправдива відповідь :) Просто ділився своїми думками щодо фундаменту, який є Jonskeet; p
Юваль Ітчаков

40
FYI BTW Я думаю, що це ти, що ти там цитуєш. :-)
Ерік Ліпперт

2
@EricLippert - Це робить цитату ще більш надійною.
Амір Попович

Домен посилання у відповіді мертвий.
Панг

25

Згідно з MSDN, це статичний список класів:

Помилка компілятора CS0702

Обмеження не може бути спеціальним ідентифікатором класу. Наступні типи не можуть використовуватися як обмеження:

  • System.Object
  • System.Array
  • Система.Делегат
  • System.Enum
  • System.ValueType.

4
Класно, здається, правильна відповідь, хороша знахідка! Але де System.MulticastDelegateв списку?
Мінц97

8
@ Mints97: ідея, можливо, відсутність документації?
Тім Шмелтер

Схоже, ви також не можете успадкувати ці класи.
Девід Клемффнер

14

Згідно зі специфікацією мови C # 4.0 (Зашифровано: [10.1.5] Обмеження параметрів типу) вказує дві речі:

1] Тип не повинен бути об’єктом. Оскільки всі типи походять від об'єкта, таке обмеження не мало б ефекту, якби воно було дозволене.

2] Якщо T не має основних обмежень або обмежень параметрів типу, його ефективним базовим класом є об'єкт.

Коли ви визначаєте загальний клас, ви можете застосувати обмеження до типів типів, які клієнтський код може використовувати для аргументів типу, коли він створює ваш клас. Якщо клієнтський код намагається інстанціювати ваш клас, використовуючи тип, який заборонений обмеженням, результат - помилка часу компіляції. Ці обмеження називаються обмеженнями. Обмеження визначаються за допомогою контекстного ключового слова де. Якщо ви хочете обмежити загальний тип бути еталонним типом, використовуйте: class.

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

Це забороняє загальному типу бути типом значення, таким як int або структура тощо.

Також обмеження не може бути спеціальним ідентифікатором класу. Наступні типи не можуть використовуватися як обмеження:

  • System.Object
  • System.Array
  • Система.Делегат
  • System.Enum
  • System.ValueType.

12

У Рамці є певні класи, які фактично передають особливі характеристики всім похідним від них типам, але самі не володіють цими характеристиками . 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 # не думали про них, вони заборонені без поважних причин.


10

У CLR ви знайдете C # 4th Edition:

Первинні обмеження

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


Дивіться також: C # LS розділ 10.1.4.1: Безпосередній базовий клас типу класу не повинен бути якимось - або з наступних типів: System.Array, System.Delegate, System.MulticastDelegate, System.Enum, або System.ValueType. Крім того, декларація загального класу не може використовуватись System.Attributeяк прямий чи непрямий базовий клас.
Jeroen Vannevel

5

Я не думаю, що існує якесь офіційне визначення "спеціальних класів" / "спеціальних типів".

Ви можете подумати про них типу aa, які не можна використовувати з семантикою "регулярних" типів:

  • ви не можете створити їх безпосередньо;
  • ви не можете успадкувати власні типи від них безпосередньо;
  • є якась магія компілятора для роботи з ними (необов'язково);
  • пряме використання їхніх примірників, щонайменше, марне (необов'язково; уявіть, що ви створили загальне вище, який загальний код ви збираєтеся написати?)

PS Я додам System.Voidдо списку.


2
System.Voidдає зовсім іншу помилку при використанні як загального обмеження =)
Mints97

@ Мінц97: правда. Але якщо питання про "особливе", то так, voidдуже особливе. :)
Денніс

@Dennis: код, у якого є пара параметрів типу, обмежених для System.Arrayвикористання методів, таких як Array.Copyпереміщення даних з одного в інший; код з параметрами типу обмежений, щоб System.Delegateмати можливість використовувати Delegate.Combineїх і передавати результат правильному типу . Ефективне використання загальновідомого типу Enumбуде, використовуючи Reflection один раз для кожного такого типу, але загальний HasAnyFlagметод може бути в 10 разів швидшим, ніж негенеричний метод.
supercat
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.