Was genau ist eine „Sonderklasse“?

114

Nachdem Sie Folgendes nicht kompiliert haben:

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

mit dem Fehler

Eine Einschränkung kann keine spezielle Klasse "System.Array" sein.

Ich begann fragen, was genau ist eine „Sonderklasse“?

Menschen scheinen oft die gleiche Art von Fehler zu bekommen, wenn sie System.Enumin einer generischen Einschränkung angeben . Ich habe die gleichen Ergebnisse mit System.Object, System.Delegate, System.MulticastDelegateund System.ValueTypeauch.

Gibt es noch mehr davon? Ich kann keine Informationen zu "Sonderklassen" in C # finden.

Was ist an diesen Klassen so besonders, dass wir sie nicht als generische Typeinschränkung verwenden können?

Mints97
quelle
14
Ich denke nicht, dass dies ein direktes Duplikat ist. Die Frage ist nicht "Warum kann ich das nicht als Einschränkung verwenden?", Sondern "Was sind diese speziellen Klassen?". Ich habe mir diese Fragen angesehen und sie geben nur an, warum es sinnlos wäre, sie als Einschränkung zu verwenden, ohne zu erklären, was eine "spezielle Klasse" tatsächlich ist und warum sie als besonders angesehen wird.
Adam Houldsworth
2
Nach meiner Erfahrung sind Klassen, die verwendet werden, aber Sie können sie nicht direkt verwenden, nur implizit über andere Syntax, spezielle Klassen. Enum fällt in die gleiche Kategorie. Was genau sie besonders macht, weiß ich nicht.
Lasse V. Karlsen
@AndyKorneyev: Diese Frage ist etwas anders. Ich bitte um eine Definition einer "Sonderklasse" und / oder eine umfassende Liste davon. Diese Frage fragt einfach nach dem Grund, warum System.Array keine generische Typeinschränkung sein kann.
Mints97
In der Dokumentation heißt es: "[...] Nur das System und die Compiler können explizit von der Array-Klasse abgeleitet werden." Es ist wahrscheinlich, dass dies eine besondere Klasse ist - sie wird vom Compiler speziell behandelt.
RB.
1
@RB.: Falsch. Diese Logik würde bedeuten, dass System.Objectes sich nicht um eine "Sonderklasse" handelt, da dies gültig ist public class X : System.Object { }, sondern System.Objectimmer noch um eine "Sonderklasse".
Mints97

Antworten:

106

Aus dem Roslyn-Quellcode geht hervor, dass es sich um eine Liste fest codierter Typen handelt:

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;
}

Quelle: Binder_Constraints.cs IsValidConstraintType
Ich habe es mithilfe einer GitHub-Suche gefunden: "Eine Einschränkung kann keine spezielle Klasse sein."

Kobi
quelle
1
@kobi 702 wird zum Compilerfehler CS0702, wie in der Compilerausgabe (die in dieser Frage nicht zitiert wurde) und anderen Antworten zu sehen ist.
AakashM
1
@AakashM - Danke! Ich habe versucht zu kompilieren und habe aus irgendeinem Grund die Fehlernummer nicht erhalten. Ich brauchte dann fast 5 Minuten, um es herauszufinden, und hatte nicht genug Zeit, um meinen Kommentar zu bearbeiten. Traurige Geschichte.
Kobi
1
@Kobi: Sie müssen sich das Ausgabefenster ansehen, dort finden Sie die genaue Compiler-Fehlercode-Nummer CS0702.
Tim Schmelter
9
Die eigentliche Frage ist nun, warum diese speziellen Klassen sind.
David sagt Reinstate Monica
@DavidGrinberg Vielleicht liegt der Grund darin, dass Sie nicht direkt von diesen Typen erben können (außer für object), oder zumindest hat es etwas damit zu tun. Auch where T : Arraywürde es ermöglichen Assay als T vorbei, die wahrscheinlich nicht ist , was die meisten Leute wollen.
IllidanS4 will Monica
42

Ich fand einen Kommentar von Jon Skeet aus dem Jahr 2008 zu einer ähnlichen Frage: Warum wird die System.EnumEinschränkung nicht unterstützt ?

Ich weiß, dass dies ein wenig vom Thema abweicht , aber er fragte Eric Lippert (das C # -Team) danach und sie gaben diese Antwort:

Zunächst einmal ist Ihre Vermutung richtig; Die Einschränkungen für Einschränkungen sind im Großen und Ganzen Artefakte der Sprache, nicht so sehr die CLR. (Wenn wir diese Funktionen ausführen würden, gäbe es einige kleinere Dinge, die wir in der CLR ändern möchten, was die Angabe von Aufzählungstypen betrifft, aber meistens wäre dies Spracharbeit.)

Zweitens würde ich persönlich gerne delegierte Einschränkungen, Aufzählungsbeschränkungen und die Möglichkeit haben, Einschränkungen anzugeben, die heute illegal sind, weil der Compiler versucht, Sie vor sich selbst zu retten. (Das heißt, versiegelte Typen als Einschränkungen legalisieren und so weiter.)

Aufgrund von Planungsbeschränkungen werden wir diese Funktionen wahrscheinlich nicht in die nächste Version der Sprache integrieren können.

Amir Popovich
quelle
10
@YuvalItzchakov - Ist es besser, Github \ MSDN zu zitieren? Das C # -Team hat eine konkrete Antwort zu diesem oder einem ähnlichen Thema gegeben. Es kann niemanden wirklich verletzen. Jon Skeet hat sie gerade zitiert und ist ziemlich zuverlässig, wenn es um C # geht.
Amir Popovich
5
Keine Notwendigkeit, sich aufzuregen. Ich habe nicht gemeint, dass dies keine gültige Antwort ist :) Ich habe nur meine Gedanken über das Fundament geteilt, das jonskeet ist; p
Yuval Itzchakov
40
Übrigens, ich denke, das bin ich, was Sie dort zitieren. :-)
Eric Lippert
2
@EricLippert - Das macht das Zitat noch zuverlässiger.
Amir Popovich
Die Domain des Links in der Antwort ist tot.
Pang
25

Laut MSDN handelt es sich um eine statische Liste von Klassen:

Compilerfehler CS0702

Einschränkung kann keine spezielle Klasse 'bezeichner' sein Die folgenden Typen dürfen nicht als Einschränkungen verwendet werden:

  • System.Object
  • System.Array
  • System.Delegate
  • System.Enum
  • System.ValueType.
Tim Schmelter
quelle
4
Cool, scheint die richtige Antwort zu sein, guter Fund! Aber wo ist System.MulticastDelegatein der Liste?
Mints97
8
@ Mints97: keine Ahnung, fehlende Dokumentation vielleicht?
Tim Schmelter
Es scheint, als könnten Sie auch nicht von diesen Klassen erben.
David Klempfner
14

Gemäß C # 4.0-Sprachspezifikation (codiert: [10.1.5] Typparametereinschränkungen) werden zwei Dinge gesagt:

1] Der Typ darf kein Objekt sein. Da alle Typen vom Objekt abgeleitet sind, hätte eine solche Einschränkung keine Auswirkung, wenn sie zulässig wäre.

2] Wenn T keine primären Einschränkungen oder Typparametereinschränkungen hat, ist seine effektive Basisklasse Objekt.

Wenn Sie eine generische Klasse definieren, können Sie Einschränkungen auf die Arten von Typen anwenden, die Clientcode für Typargumente verwenden kann, wenn er Ihre Klasse instanziiert. Wenn der Clientcode versucht, Ihre Klasse mithilfe eines Typs zu instanziieren, der von einer Einschränkung nicht zugelassen wird, ist das Ergebnis ein Fehler bei der Kompilierung. Diese Einschränkungen werden als Einschränkungen bezeichnet. Einschränkungen werden mithilfe des Schlüsselworts where contextual angegeben. Wenn Sie einen generischen Typ als Referenztyp einschränken möchten, verwenden Sie: class.

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

Dies verhindert, dass der generische Typ ein Werttyp ist, wie z. B. int oder eine Struktur usw.

Außerdem kann die Einschränkung keine spezielle Klassen-ID sein. Die folgenden Typen dürfen nicht als Einschränkungen verwendet werden:

  • System.Object
  • System.Array
  • System.Delegate
  • System.Enum
  • System.ValueType.
Rahul Nikate
quelle
12

Es gibt bestimmte Klassen im Framework, die spezielle Merkmale effektiv an alle von ihnen abgeleiteten Typen weitergeben, diese Merkmale jedoch selbst nicht besitzen . Die CLR selbst schreibt nicht vor, diese Klassen als Einschränkungen zu verwenden, aber auf sie beschränkte generische Typen würden die nicht vererbten Merkmale nicht wie konkrete Typen erhalten. Die Entwickler von C # entschieden, dass sie solche Einschränkungen verbieten sollten, anstatt ihnen zu erlauben, sich so zu verhalten, wie sie es in der CLR tun, da ein solches Verhalten einige Menschen verwirren könnte und sie keinen Nutzen daraus ziehen konnten.

Wenn man zum Beispiel schreiben darf : void CopyArray<T>(T dest, T source, int start, int count); man könnte Methoden destund sourceMethoden übergeben, die ein Argument vom Typ erwarten System.Array; Ferner würde eine Kompilierung-Validierung , dass bekommen destund sourcewaren die kompatibelen Array - Typen, aber man würde nicht dem Zugriff auf Elemente des Arrays mit der Lage sein []Bediener.

Die Unfähigkeit, Arrayals Einschränkung zu verwenden, ist meistens recht einfach zu umgehen, da dies void CopyArray<T>(T[] dest, T[] source, int start, int count)in fast allen Situationen funktioniert, in denen die erstere Methode funktionieren würde. Es hat jedoch eine Schwäche: Die erstere Methode würde in dem Szenario funktionieren, in dem eines oder beide der Argumente vom Typ waren, System.Arraywährend Fälle zurückgewiesen wurden, in denen die Argumente inkompatible Array-Typen sind. Durch Hinzufügen einer Überladung, bei der beide Argumente vom Typ waren, System.Arraywürde der Code die zusätzlichen Fälle akzeptieren, die er akzeptieren sollte, aber auch fälschlicherweise Fälle akzeptieren, die er nicht akzeptieren sollte.

Ich finde die Entscheidung, die meisten besonderen Einschränkungen zu verbieten, lästig. Die einzige, die keine semantische Bedeutung haben würde, wäre System.Object[denn wenn dies als Einschränkung legal wäre, würde alles sie erfüllen]. System.ValueTypeDies wäre wahrscheinlich nicht sehr nützlich, da Referenzen vom Typ ValueTypenicht wirklich viel mit Werttypen gemeinsam haben, aber in Fällen, in denen es um Reflexion geht, plausibel einen gewissen Wert haben könnten. Beide System.Enumund System.Delegatehätten einige echte Verwendungszwecke, aber da die Macher von C # nicht an sie gedacht haben, sind sie ohne guten Grund verboten.

Superkatze
quelle
10

Folgendes kann in CLR über C # 4th Edition gefunden werden:

Primäre Einschränkungen

Ein Typparameter kann null primäre Einschränkungen oder eine primäre Einschränkung angeben. Eine primäre Einschränkung kann ein Referenztyp sein, der eine Klasse identifiziert, die nicht versiegelt ist. Sie können keinen der folgenden speziellen Referenztypen angeben: System.Object , System.Array , System.Delegate , System.MulticastDelegate , System.ValueType , System.Enum oder System.Void . Wenn Sie eine Einschränkung für den Referenztyp angeben, versprechen Sie dem Compiler, dass ein angegebenes Typargument entweder vom gleichen Typ oder von einem vom Einschränkungstyp abgeleiteten Typ ist.

Claudio P.
quelle
Siehe auch: C # LS Abschnitt 10.1.4.1: Die direkte Basisklasse eines Klassentypen nicht mit einer der folgenden Typen sein muß: System.Array, System.Delegate, System.MulticastDelegate, System.Enum, oder System.ValueType. Darüber hinaus kann eine generische Klassendeklaration nicht System.Attributeals direkte oder indirekte Basisklasse verwendet werden.
Jeroen Vannevel
5

Ich glaube nicht, dass es eine offizielle Definition von "Sonderklassen" / "Sondertypen" gibt.

Sie können über sie als Typen nachdenken, die nicht mit der Semantik "regulärer" Typen verwendet werden können:

  • Sie können sie nicht direkt instanziieren.
  • Sie können benutzerdefinierte Typen nicht direkt von ihnen erben.
  • Es gibt einige Compiler-Magie, um mit ihnen zu arbeiten (optional);
  • Die direkte Verwendung ihrer Instanzen ist zumindest nutzlos (optional; stellen Sie sich vor, Sie haben oben einen generischen Code erstellt. Welchen generischen Code werden Sie schreiben?)

PS Ich würde System.Voidder Liste hinzufügen .

Dennis
quelle
2
System.Voidgibt einen ganz anderen Fehler, wenn als generische Einschränkung verwendet =)
Mints97
@ Mints97: wahr. Aber wenn die Frage "speziell" ist, dann ist ja voidganz besonders. :)
Dennis
@Dennis: Code, der einige Parameter eines Typs enthält, auf den er beschränkt ist, System.Arraykann Methoden verwenden Array.Copy, um Daten von einem zum anderen zu verschieben. Code mit den Parametern eines Typs gezwungen zu der System.DelegateLage wäre , zu verwenden , Delegate.Combineauf sie und warf das Ergebnis in den richtigen Typ . Wenn Sie einen generischen bekannten Typ effektiv nutzen, Enumwird Reflection für jeden dieser Typen einmal verwendet. Eine generische HasAnyFlagMethode kann jedoch 10x schneller sein als eine nicht generische Methode.
Superkatze