160 votes

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

Il s'agit d'une question de syntaxe. J'ai une classe générique qui hérite d'une classe de base générique et qui applique une contrainte à l'un des paramètres de type. Je souhaite également que la classe dérivée mette en œuvre une interface. Je n'arrive pas à trouver la bonne syntaxe.

Voici ce que j'ai :

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

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

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

Mais cela n'est pas correct, car T2 devrait alors implémenter à la fois IBar et IFoo, et non pas DerivedFoo pour implémenter IFoo.

J'ai essayé de chercher sur Google, d'utiliser les deux points, les points-virgules, etc, mais je n'ai rien trouvé. Je suis sûr que la réponse est d'une simplicité déconcertante.

228voto

Adam Robinson Points 88472

Vous devez inclure la signature complète de votre classe avant de définir les contraintes génériques.

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

16voto

Eric Lippert Points 300275

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

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

  • attributs, entre crochets
  • les modificateurs ("public", "static", etc.)
  • "partiel"
  • "classe"
  • le nom de la classe
  • une liste de déclarations de paramètres de type séparées par des virgules et placées entre crochets angulaires
  • deux points suivis d'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 être placée en premier s'il y en a une)
  • contraintes liées aux paramètres de type
  • le corps de la classe, entouré d'accolades
  • un point-virgule

Tous les éléments de cette liste sont facultatifs, à l'exception de la "classe", du nom et du corps, mais ils doivent tous figurer dans cet ordre s'ils apparaissent.

10voto

Stan R. Points 8967
public interface IFoo {}
public interface IBar {}

public class ParentFoo<T,T1> { }
public class DerivedFoo<T, T1> : ParentFoo<T, T1>, IFoo where T1 : IBar { }

3voto

Stokely Points 405

RÈGLES GÉNÉRIQUES D'HÉRITAGE

Il existe d'autres règles que vous devez suivre lorsque vous héritez de classes de type générique en C#. N'oubliez pas que vos classes de base et dérivées peuvent utiliser des types génériques différents ou les partager. Les tests suivants m'ont aidé à comprendre les règles. Les classes génériques peuvent hériter de classes de base concrètes, construites fermées ou construites ouvertes comme suit :

// First create some Base Classes to inherit from.
// Non-generic and generic classes are included.
// Below, I will inherit these in Derived Classes.
// If my inheritance test works, you will see "YES", otherwise "NO".

class BaseType1 { }
class BaseType2<T> { }
class BaseType3<T1,T2> { }

// -------------------------------------------------

// Concrete type inheritance. Here the inheriting type 
// does NOT have to be generic as the child

class Derived1 : BaseType1 { }// YES!
class Derived2<T> : BaseType1 { }// YES!

// Open constructed type generic inheritance allows shared 
// generic type inheritance.
class Derived3<T> : BaseType2<T> { }// YES!

// Closed constructed type inheritance. Note that what the child 
// class accepts as far as types is different from what the 
// parent accepts.
// This is allowed as each implements its own types and 
// constraints on its members and the parent has defined its type.
class Derived4<T> : BaseType2<int> { }// YES!

// NO! Base type must know its accepted type if the child
// class is not accepting a generic "T" type as well.
class Derived5 : BaseType2<T> { }// NO!

// Another form that limits generic type. Note, here the first 
// base type uses a reference type "string" that is different from
// what the derived child type uses, which is any "struct" type or
// int, etc. Because the base type is defined as "string" the 
// child can define anything for its generic or not even use a generic.

// The "where" clause only affects the derived class type!
class Derived7<T> : BaseType2<string> where T : struct { }// YES!
class Derived6<T> : BaseType2<T> where T : struct { }// YES!

// NO! The "where" clause cannot be used to define the base class's type!
class Derived8 : BaseType2<T> where T : struct { }// NO!

// NO! "T1" and "T2"must be a concrete type again if the derived 
// type doesn't use the same
class Derived9 : BaseType3<T1,T2>// NO!

// Here, both parent and child classes must accept one generic 
// type for this to work.
class Derived10<T> : BaseType3<T,T> { }// YES!

// Each generic type must have unique names in the derived class 
// AND match the base class names. That is why these fail.
class Derived11<T, T> : BaseType3<T1, T2> { }// NO!
class Derived12<name1, name2> : BaseType3<T1, T2> { }// NO!

class Derived13<T1, T2> : BaseType3<T1, T2> { }// YES!

// As long as one generic name matches between child and parent, 
// this shared generic type on child and parent ok.
class Derived14<Tname,T2> : BaseType2<Tname> { }// YES!

// Again, the child class doesn't have to be generic if the base 
// generic class has defined the types it accepts.
class Derived15 : BaseType3<string, int> { }// YES!

class Derived16<T1, T2> : BaseType3<string, int> { }// YES!

// Here, you can have the child class accept two generic types, 
// while the parent uses its own explicit type.
class Derived17<T1, T2> : BaseType2<string> { }// YES!

// No, you need to define these using "where".
class Derived18<string, int> : BaseType1 { }// NO!

class Derived19<T1, T2> : BaseType1 where T1 : class where T2 : struct { }// YES!

// This generates an error. You cannot inherit a generic type.
class Derived20 : T {}// NO!

2voto

user875234 Points 667
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; }
}

Il s'agit d'une extension des réponses existantes. La réponse par défaut est string si vous ne fournissez pas de type. Je n'ai pas implémenté d'interface, mais cela ne devrait rien exiger de plus que d'habitude.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X