Après avoir échoué à obtenir quelque chose comme ce qui suit à compiler:
public class Gen<T> where T : System.Array
{
}
avec l'erreur
Une contrainte ne peut pas être une classe spéciale `System.Array '
Je commençais à me demander, qu'est - ce exactement est une « classe spéciale »?
Les gens semblent souvent obtenir le même type d'erreur lorsqu'ils spécifient System.Enum
dans une contrainte générique. Je suis les mêmes résultats avec System.Object
, System.Delegate
, System.MulticastDelegate
et System.ValueType
aussi.
Y en a-t-il plus? Je ne trouve aucune information sur les "classes spéciales" en C #.
De plus, qu'est - ce qui est si spécial dans ces classes que nous ne pouvons pas les utiliser comme contrainte de type générique?
c#
class
generics
generic-constraints
Mints97
la source
la source
System.Object
n'est pas une "classe spéciale", comme cela est valide:,public class X : System.Object { }
maisSystem.Object
est toujours une "classe spéciale".Réponses:
D'après le code source de Roslyn, cela ressemble à une liste de types codés en dur:
Source: Binder_Constraints.cs IsValidConstraintType
Je l'ai trouvé en utilisant une recherche GitHub: "Une contrainte ne peut pas être une classe spéciale"
la source
CS0702
.object
), ou du moins cela a quelque chose à voir avec cela. En outrewhere T : Array
permettrait le passage de dosage comme T, ce qui est probablement pas ce que la plupart des gens veulent.J'ai trouvé un commentaire de Jon Skeet de 2008 sur une question similaire: pourquoi la
System.Enum
contrainte n'est-elle pas prise en charge?Je sais que c'est un peu hors sujet , mais il a interrogé Eric Lippert (l'équipe C #) à ce sujet et ils ont fourni cette réponse:
la source
Selon MSDN, c'est une liste statique de classes:
Erreur du compilateur CS0702
La contrainte ne peut pas être un 'identifiant' de classe spéciale Les types suivants ne peuvent pas être utilisés comme contraintes:
la source
System.MulticastDelegate
la liste?Selon la spécification du langage C # 4.0 (codé: [10.1.5] Contraintes de paramètre de type), deux choses:
Lorsque vous définissez une classe générique, vous pouvez appliquer des restrictions aux types de types que le code client peut utiliser pour les arguments de type lorsqu'il instancie votre classe. Si le code client tente d'instancier votre classe à l'aide d'un type qui n'est pas autorisé par une contrainte, le résultat est une erreur de compilation. Ces restrictions sont appelées contraintes. Les contraintes sont spécifiées à l'aide du mot clé contextuel where. Si vous souhaitez contraindre un type générique à être un type de référence, utilisez: class.
Cela empêchera le type générique d'être un type valeur, tel que int ou une structure, etc.
De plus, la contrainte ne peut pas être un 'identificateur' de classe spéciale Les types suivants ne peuvent pas être utilisés comme contraintes:
la source
Il existe certaines classes dans le Framework qui transmettent effectivement des caractéristiques spéciales à tous les types qui en dérivent mais ne possèdent pas ces caractéristiques elles-mêmes . Le CLR lui-même n'impose aucune interdiction d'utiliser ces classes comme contraintes, mais les types génériques qui y sont contraints n'acquériraient pas les caractéristiques non héritées comme le feraient des types concrets. Les créateurs de C # ont décidé que, parce qu'un tel comportement pouvait semer la confusion chez certaines personnes et qu'ils ne voyaient aucune utilité, ils devraient interdire de telles contraintes plutôt que de leur permettre de se comporter comme ils le font dans le CLR.
Si, par exemple, on était autorisé à écrire
void CopyArray<T>(T dest, T source, int start, int count)
:; on pourrait passerdest
etsource
à des méthodes qui attendent un argument de typeSystem.Array
; de plus, on obtiendrait la validation à la compilation quidest
etsource
étaient les types de tableaux compatibles, mais on ne pourrait pas accéder aux éléments du tableau en utilisant l'[]
opérateur.L'incapacité à utiliser
Array
comme contrainte est généralement assez facile à contourner, carvoid CopyArray<T>(T[] dest, T[] source, int start, int count)
fonctionnera dans presque toutes les situations où l'ancienne méthode fonctionnerait. Elle a cependant une faiblesse: la première méthode fonctionnerait dans le scénario où l'un des arguments ou les deux étaient de typeSystem.Array
tout en rejetant les cas où les arguments sont des types de tableaux incompatibles; l'ajout d'une surcharge où les deux arguments étaient de typeSystem.Array
ferait en sorte que le code accepte les cas supplémentaires qu'il devrait accepter, mais aussi le ferait accepter par erreur les cas qu'il ne devrait pas.Je trouve la décision d'interdire la plupart des contraintes spéciales ennuyeuse. Le seul qui aurait une signification sémantique nulle serait
System.Object
[puisque si c'était légal comme contrainte, tout le satisferait].System.ValueType
ne serait probablement pas très utile, car les références de typeValueType
n'ont pas vraiment grand chose en commun avec les types valeur, mais cela pourrait vraisemblablement avoir une certaine valeur dans les cas impliquant Reflection. Les deuxSystem.Enum
etSystem.Delegate
aurait des utilisations réelles, mais étant donné que les créateurs de C # ne pensaient pas d'eux , ils sont mis hors la loi sans raison.la source
Les éléments suivants peuvent être trouvés dans CLR via C # 4th Edition:
Contraintes primaires
Un paramètre de type peut spécifier zéro contrainte principale ou une contrainte principale. Une contrainte principale peut être un type de référence qui identifie une classe qui n'est pas scellée. Vous ne pouvez pas spécifier l'un des types de référence spéciaux suivants: System.Object , System.Array , System.Delegate , System.MulticastDelegate , System.ValueType , System.Enum ou System.Void . Lorsque vous spécifiez une contrainte de type de référence, vous promettez au compilateur qu'un argument de type spécifié sera soit du même type, soit d'un type dérivé du type de contrainte.
la source
System.Array
,System.Delegate
,System.MulticastDelegate
,System.Enum
ouSystem.ValueType
. De plus, une déclaration de classe générique ne peut pas être utiliséeSystem.Attribute
comme classe de base directe ou indirecte.Je ne pense pas qu'il existe une définition officielle des "classes spéciales" / "types spéciaux".
Vous pouvez penser à eux des types aa, qui ne peuvent pas être utilisés avec la sémantique des types "réguliers":
PS j'ajouterais
System.Void
à la liste.la source
System.Void
donne une erreur entièrement différente lorsqu'il est utilisé comme contrainte générique =)void
c'est très spécial. :)System.Array
pourrait utiliser des méthodes commeArray.Copy
déplacer des données de l'un à l'autre; le code avec des paramètres d'un type contraint àSystem.Delegate
pourrait être utiliséDelegate.Combine
sur eux et convertir le résultat dans le type approprié . Utiliser efficacement un type générique connuEnum
utilisera Reflection une fois pour chacun de ces types, mais uneHasAnyFlag
méthode générique peut être 10 fois plus rapide qu'une méthode non générique.