2010-04-15 4 views
12

Ceci est une extension de this question et probablement pourrait même être une copie d'une autre question (Si oui, s'il vous plaît pardonnez-moi). Je see from MSDN que les génériques sont généralement utilisés avec des collectionsGénériques vs héritage (quand aucune classe de collection n'est impliquée)

L'utilisation la plus courante pour génériques des classes est avec des collections comme listes chaînées, tables de hachage, des piles, files d'attente, des arbres et ainsi de suite où opérations telles que l'ajout et enlever articles de la collection sont effectué de la même manière indépendamment du type de données étant stocké.

Les exemples que j'ai vus valident également l'énoncé ci-dessus.

Est-ce que quelqu'un peut donner une utilisation valide des génériques dans un scénario réel qui n'implique aucune collection?

pédante, je pensais à faire un exemple qui ne concerne pas les collections

public class Animal<T> 
{ 
    public void Speak() 
    { 
     Console.WriteLine("I am an Animal and my type is " + typeof(T).ToString()); 
    } 

    public void Eat() 
    { 
     //Eat food 
    } 
} 

public class Dog 
{ 
    public void WhoAmI() 
    { 
     Console.WriteLine(this.GetType().ToString()); 

    } 
}   

et "Un animal de type chien" sera

Animal<Dog> magic = new Animal<Dog>();

Il est tout à fait possible pour avoir Dog hérité de Animal (En supposant une version non générique de Animal) Dog:Animal Par conséquent Dogest unAnimal

Un autre exemple que je pensais était un BankAccount. Il peut être BankAccount<Checking>, BankAccount<Savings>. Cela peut très bien être Checking:BankAccount et Savings:BankAccount.

Y a-t-il des meilleures pratiques pour déterminer si nous devrions aller avec des génériques ou avec l'héritage?

+1

Utilisez héritage chaque fois que vous avez besoin d'étendre une classe. Dans votre exemple, Animal est un générique, c'est-à-dire qu'il peut être n'importe quel type que vous lui attribuez. Les génériques permettent à votre programme d'utiliser une interface commune pour effectuer des opérations. – Kaili

Répondre

8

Vous obtiendrez probablement de meilleures réponses, mais en attendant, considérez ceci: Les classes génériques impliquent une relation «of» entre la classe générique et la classe de paramètres.

Tous les chiens sont des animaux, ils partagent donc certains attributs/qualités avec tous les autres animaux. Si vous utilisez l'héritage, alors il est très facile de mettre en œuvre ces qualités communes dans la classe animale, et ajouter des qualités dans les classes décentes.Mais si vous le mettre en œuvre en utilisant Animal (Of Dog), puis:

  1. Votre classe Dog est pas vraiment un chien entièrement qualifié/animal en lui-même, comme ses « qualités animales » sont contenues dans la classe Animal (Of T). Le chien doit être attaché avec l'animal dans une relation parent-enfant.
  2. Votre classe Animal n'est pas vraiment un animal: vous ne pouvez pas créer une méthode qui accepte les animaux comme argument, car vous ne pouvez pas vous référer à la classe Animal (Of T) globalisée. C'est en fait une famille de classes avec un comportement commun, pas une superclasse. Vous perdez l'avantage de traiter avec des animaux en dehors de la classe Animal (Of T).

Mais voici comment, à mon humble avis, vous devriez penser des médicaments génériques: Pensez à une classe MedicalDoctor(Of T). Veterinarian(Of T as Animal) hériterait de MedicalDoctor(Of Animal). DogVeterinarian hériterait de Veterinarian(Of Dog). Point clé: la classe générique et la classe de paramètres ne sont pas étroitement couplées et co-dépendantes, elles coexistent. Par ailleurs, si vous voulez voir une bonne utilisation des génériques hors collection, notez simplement les délégués que nous avons: Action (Of T), Func (Of TResult), Comparaison (Of T), EventHandler (Of TEventArgs) , Converter (Of TSource, TResult) ... et les interfaces: IEqualityComparer (Of T), IComparer (Of T) ...

3

Un exemple réel du monde est connu dans le WCF Channel Factory.

De la page:

public sealed class Test 
{ 
    static void Main() 
    { 
     // Code not shown. 
    } 

    public void Run() 
    { 
     // This code is written by an application developer. 
     // Create a channel factory. 
     BasicHttpBinding myBinding = new BasicHttpBinding(); 

     EndpointAddress myEndpoint = new EndpointAddress("http://localhost/MathService/Ep1"); 

     ChannelFactory<IMath> myChannelFactory = new ChannelFactory<IMath>(myBinding, myEndpoint); 

     // Create a channel. 
     IMath wcfClient1 = myChannelFactory.CreateChannel(); 
     double s = wcfClient1.Add(3, 39); 
     Console.WriteLine(s.ToString()); 
     ((IClientChannel)wcfClient1).Close(); 

     // Create another channel. 
     IMath wcfClient2 = myChannelFactory.CreateChannel(); 
     s = wcfClient2.Add(15, 27); 
     Console.WriteLine(s.ToString()); 
     ((IClientChannel)wcfClient2).Close(); 
     myChannelFactory.Close(); 
    } 
} 

La façon dont je pense génériques est qu'ils sont représentent un type qu'une classe agit sur. Par exemple, ChannelFactory crée des fabriques de type T. L'héritage représente une relation hiérarchique entre types. Un chien est un animal, un golden retriever est à la fois un chien et un animal.

3

Nullable<T> est générique, n'est pas une classe de collecte et ne peut pas impliquer l'héritage puisqu'il concerne struct s.

Les familles des délégués génériques sont très belles - EventHandler<T>, Action<...]>, Func<[...]> - il est beaucoup plus claire ce Func<int,bool> est plutôt que d'une coutume IdPredicate ou autre chose.

+0

Si vous le tournez sur sa tête, secouez-le, remuez-le et louchez-le * juste de la bonne façon *, alors vous pourriez interpréter 'Nullable ' comme une collection de non ou exactement un élément. Bien que, sauf si vous êtes un programmeur Haskell, cela ne vous arrivera probablement pas :-) –

1

Vous confondez l'aspect «est-a» de l'héritage avec l'aspect «of» des génériques.

Les génériques impliquent la même chose des opérations entre les classes, et pas seulement que les opérations existent de manière polymorphique. Dans votre exemple Animal, une instance de Animal<Dog> n'a pas de méthode WhoAmI, alors qu'une instance de Dog : Animal le ferait.

0

Utilisez l'héritage dans votre situation, s'il vous plaît.
Si une classe peut être décrite correctement par une autre classe [par exemple, un carré peut être décrit comme un rectangle], le carré doit hériter/s'étendre de la quête du rectangle. Sinon, vous tombez dans le pétrin.

Utilisez GenericClass pour signifier this is a GenericClass object of tType-s Utiliser Square: Rectangle pour signifier this is a Square, which is also a Rectangle

Dans votre cas:
utilisation Chien: Animal pour signifier this is a Dog, which is also an Animal

Questions connexes