2009-03-18 14 views
3

(. Merci à tous pour les réponses, here is my refactored example, à son tour, une autre question StackOverflow sur le principe de responsabilité unique)Quels sont les avantages de l'utilisation de génériques dans les signatures de méthodes?

En venant de PHP en C#, cette syntaxe intimidait:

container.RegisterType<Customer>("customer1"); 

jusqu'à ce que je compris il exprime la même chose que:

container.RegisterType(typeof(Customer), "customer1"); 

comme je le démontre dans le code ci-dessous.

Alors est-il une raison pour laquelle les médicaments génériques est utilisé ici (par exemple à travers l'unité et la plupart des conteneurs C# IoC) autre que étant juste une syntaxe plus propre, à savoir que vous n'avez pas besoin typeof() lors de l'envoi du type?

using System; 

namespace TestGenericParameter 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Container container = new Container(); 
      container.RegisterType<Customer>("test"); 
      container.RegisterType(typeof(Customer), "test"); 

      Console.ReadLine(); 
     } 
    } 

    public class Container 
    { 
     public void RegisterType<T>(string dummy) 
     { 
      Console.WriteLine("Type={0}, dummy={1}, name of class={2}", typeof(T), dummy, typeof(T).Name); 
     } 

     public void RegisterType(Type T, string dummy) 
     { 
      Console.WriteLine("Type={0}, dummy={1}, name of class={2}", T, dummy, T.Name); 
     } 

    } 

    public class Customer {} 
} 

//OUTPUT: 
//Type=TestGenericParameter.Customer, dummy=test, name of class=Customer 
//Type=TestGenericParameter.Customer, dummy=test, name of class=Customer 

Répondre

5

Une raison principale est la sécurité de type au moment de la compilation. Si vous passez deux objets Type, vous placez la responsabilité sur le développeur au lieu du compilateur.

C'est aussi la raison pour laquelle de nombreux conteneurs IoC l'utilisent, car votre compilateur se plaindra si un type concret n'hérite pas du type abstrait.

public void Register<TAbstract, TConcrete>() where TConcrete : TAbstract 
{ 
} 

Ce code ne fonctionnera que si TConcrete met actuellement en œuvre ou héritant TAbstract. Si cette méthode a pris deux paramètres Type, votre méthode doit valider cette relation.

3

Pour un exemple, comparer le code nécessaire pour créer une instance de votre type en utilisant l'option typeof par rapport à un générique. Ou renvoyez une instance du type. Ou acceptez une instance du type en tant qu'argument. Ou définissez une propriété sur une instance du type.

En général, si vous travaillez uniquement avec le type lui-même, vous pouvez accepter un paramètre de type. Si vous voulez faire quelque chose avec une instance du type, utilisez un générique.

Une autre raison d'utiliser un générique est si vous souhaitez appliquer des contraintes au type. Par exemple, vous pouvez demander au type d'implémenter une ou plusieurs interfaces, hériter d'un autre type, être un type de référence ou un type de valeur, avoir un constructeur par défaut ou une combinaison de ce qui précède. Le compilateur appliquera ceci afin que vous ne puissiez pas construire de code qui ne soit pas conforme à vos exigences.

3

Je pense que l'une des principales utilisations est la sécurité de type avec des arguments et des valeurs de retour. Dans votre exemple, les génériques ne sont pas très utilisés, car les types d'entrée/sortie (chaîne) ne correspondent pas au cas générique (clients).

Une utilisation plus appropriée pourrait être:

public T RegisterType<T>(string name) 
{ 
    T obj = new T(); 
    obj.DoSomething(); 
    return obj; 
} 

ou peut-être

public void DoSomething<T>(T obj) 
{ 
    //operate on obj 
} 
+0

cela semble très utile, mais en essayant de l'implémenter, la ligne "T obj = new T();" me donne "Impossible de créer une instance du type de variable 'T' car elle n'a pas la nouvelle contrainte()". Qu'est-ce que je rate? –

+1

Vous devez ajouter une contrainte comme dans ma réponse: 'public T CreateNew () où T: new()' –

4

Une raison lorsque les médicaments génériques sont très utiles est lorsque le paramètre de type générique est utilisé comme le type d'un paramètre ou le type de retour de la méthode. Cela signifie que vous pouvez écrire des méthodes telles que

public T GetAs<T>(string name) 

où le type de retour peut être contrôlé par le compilateur et le type valeur de boxe peuvent parfois être évités. L'appelant écrirait:

int value = GetAs<int>("foo"); 

whithout génériques, vous devez écrire

public object GetAs(Type t, string name) 

et l'appelant doit jeter le résultat à nouveau:

int value = (int)GetAs(typeof(int), "foo"); 
2

Si vous ne l'avez pas utilisez Generics, vous devez soit surcharger une méthode pour chaque type que vous voulez prendre en charge, soit accepter le paramètre en tant qu'objet et effectuer une logique de conversion.

4

Une réponse simple est inférence de type lorsque cela est possible.

Si le type générique est utilisé dans la signature de méthode, vous pouvez l'omettre parce que le type pourrait être déduit:

void SomeMethod<T>(T x, T y) where T : IComparable<T> { 
    Console.WriteLine("Result: {0} to {1} is {2}", x, y, x.CompareTo(y)); 
} 

Ainsi, l'utilisation est simplifiée:

SomeMethod(3, 4);   // instead of SomeMethod<int>(3, 4); 
SomeMethod("one", "two"); // instead of SomeMethod<string>("one", "two"); 

Si le type générique paramètre n'est pas utilisé dans la signature de la méthode l'inférence de type n'est pas possible:

var emptySequence = Enumerable.Empty<int>(); 
1

Je dirais t La meilleure raison est la sécurité du type, en utilisant le mot-clé "where", pour s'assurer que le type générique est d'un certain type (ou sous-classe/implémenteur). Utiliser "typeof" vous permettra d'envoyer n'importe quoi.

Questions connexes