Hériter d'une classe de base générique, appliquer une contrainte et implémenter une interface en C #

117

C'est une question de syntaxe. J'ai une classe générique qui hérite d'une classe de base générique et applique une contrainte à l'un des paramètres de type. Je souhaite également que la classe dérivée implémente une interface. Pour la vie de moi, je n'arrive pas à trouver la syntaxe correcte.

Voici ce que j'ai:

DerivedFoo<T1,T2> : ParentFoo<T1, T2> where T2 : IBar { ... }

La première chose qui m'est venue à l'esprit était la suivante:

DerivedFoo<T1,T2> : ParentFoo<T1, T2> where T2 : IBar, IFoo { ... }

Mais c'est incorrect car cela oblige T2 à implémenter à la fois IBar et IFoo, et non DerivedFoo pour implémenter IFoo.

J'ai essayé un peu de googler, d'utiliser des deux-points, des points-virgules, etc., mais je suis arrivé à court. Je suis sûr que la réponse est très simple.

Dan Rigby
la source
Je ne pouvais pas comprendre la réponse de @ Adam quand j'ai regardé une fois, mais après 2 minutes, je pouvais obtenir ce que c'était, merci pour la réponse. La classe dérivée a plus d'une implémentation peut-être est-ce le point. Quoi qu'il en soit, je veux montrer sa notation pour les autres. "classe DerivedClass <Type>: ParentClass où Type: IType". Rien ne doit se trouver entre la dernière classe implémentée et la clause where.
nurisezgin

Réponses:

173

Vous incluez la signature entière de votre classe avant de définir des contraintes génériques.

class DerivedFoo<T1, T2> : ParentFoo<T1, T2>, IFoo where T2 : IBar
{
    ...
}
Adam Robinson
la source
5
Pour d'autres, j'ai intériorisé cela comme une classe n'obtient qu'une seule clause where, et elle va à la fin pour toutes les contraintes de type générique.
Andy V
@Visser Il est permis d'avoir plusieurs clauses where, classe Test <T1, T2> où T1: Interface1 où T2: Interface2
bwing
@Visser ouais, ce que bwing a dit, aussi chaque clause where peut avoir plusieurs contraintes… donc la syntaxe du message d'origine est correcte, cela signifie simplement quelque chose de différent que l'op voulait. where T2 : IBar, IFoo signifie simplement qu'il T2faut implémenter les deux interfaces au lieu de l' DerivedFoo<T1,T2> implémenterIFoo
v01pe
18

Ma recommandation: lorsque vous avez une question sur la syntaxe du langage C #, lisez la spécification; c'est pourquoi nous le publions. Vous voudrez lire la section 10.1.

Pour répondre à votre question spécifique, l'ordre des choses dans une déclaration de classe est:

  • attributs, entre crochets
  • modificateurs ("public", "statique", etc.)
  • "partiel"
  • "classe"
  • le nom de la classe
  • une liste séparée par des virgules de déclarations de paramètres de type entre crochets angulaires
  • un deux-points a suivi une liste de types de base séparés par des virgules (classe de base et interfaces implémentées, la classe de base doit commencer s'il y en a une)
  • contraintes de paramètre de type
  • le corps de la classe, entouré d'accolades
  • un point-virgule

Tout sur cette liste est facultatif à l'exception de "classe", le nom et le corps, mais tout doit apparaître dans cet ordre s'il apparaît.

Eric Lippert
la source
95
Eric, bien que je vous respecte beaucoup en tant que professionnel et apprécie vos commentaires, je ne peux m'empêcher d'être frustré par ce qui apparaît comme une réponse abrasive. Vous me critiquez pour avoir choisi de poser une question sur un site de questions-réponses de programmation sur la localisation, le téléchargement et la recherche dans un document Word de 503 pages hautement technique enfoui dans un lien dans MSDN. C'est assez dur. C'était l'utilisation la plus efficace de mon temps et cela a l'avantage supplémentaire d'aider quelqu'un d'autre plus tard. Le lien vers la spécification C # Lang pour les personnes intéressées est: msdn.microsoft.com/en-us/vcsharp/aa336809.aspx
Dan Rigby
17
Aucune critique n'était voulue. Il y a un biais omniprésent dans la communication en texte pur qui fait que de simples déclarations de faits semblent brusques et abrasives; J'essaie de lire charitablement lorsqu'on me présente une liste de faits utiles, et je vous recommande de le faire également. Je maintiens ma recommandation; si vous avez des questions sur la syntaxe, la spécification y répond définitivement et commence par une table des matières utile pour localiser les définitions de syntaxes spécifiques.
Eric Lippert
3
Dan, trouver une spécification C # est aussi simple que d'entrer «C # Spec» dans Google et d'appuyer sur le bouton «J'ai de la chance». Et si vous êtes un développeur C # professionnel, vous devriez déjà avoir une spécification C # au format PDF sur votre machine. De plus, je ne veux pas non plus vous critiquer. Je n'étais pas habitué à lire les spécifications plus tôt mais j'ai commencé à les lire grâce à Jon, Eric et Pavel qui citent toujours les spécifications C # pour toute question. J'ai trouvé que la spécification C #, même si elle peut parfois être difficile à lire, est un excellent moyen d'en apprendre davantage sur le langage.
SolutionYogi
@Eric Lippert: Très bien. Merci pour votre réponse. À titre de suggestion constructive, il serait utile que Microsoft intègre le contenu de la spécification directement dans MSDN en plus de le faire exister sous forme de téléchargement séparé. La version Visual Studio .Net MSDN a une version intégrée de la spécification, mais pas des versions ultérieures. J'ai pensé à acheter le livre d'Anders Hejlberg, mais avec .Net 4.0 au coin de la rue, je suis réticent pour l'instant. amazon.com/C-Programming-Language-3rd/dp/0321562992 Merci.
Dan Rigby
2
C ++ nécessite qu'une déclaration de classe se termine par un point-virgule. De nombreux développeurs C # viennent d'un arrière-plan C ++; parfois leurs doigts insèrent le point-virgule sans que leur cerveau ne soit impliqué. :-) Il existe un certain nombre de constructions en C # qui prennent un semi optionnel là où C ++ en a besoin. C'est à peu près une commodité subtile. Je suppose que cela vous permet également d'appeler subtilement quand une déclaration de type se termine par rapport à une déclaration de corps de méthode.
Eric Lippert
8
public interface IFoo {}
public interface IBar {}

public class ParentFoo<T,T1> { }
public class DerivedFoo<T, T1> : ParentFoo<T, T1>, IFoo where T1 : IBar { }
Stan R.
la source
2
public class KeyAndValue<T>
{
    public string Key { get; set; }
    public virtual T Value { get; set; }
}

public class KeyAndValue : KeyAndValue<string>
{
    public override string Value { get; set; }
}

Ceci est une extension des réponses existantes. Il est par défaut stringsi vous ne fournissez pas de type. Je n'ai pas implémenté d'interface mais cela ne devrait nécessiter rien de différent que d'habitude.

user875234
la source