J'ai beaucoup de tests unitaires qui testent à peu près le même comportement. Cependant, le type de données change.
J'essaie de créer une méthode générique qui peut prendre n'importe quel type de données. J'ai essayé de faire mon paramètre d'entrée var mais ce n'est pas autorisé. Aussi, regardé dans les génériques c # mais qui traite généralement d'une liste.
Vous pouvez faire du paramètre un object
:
public void DoSomething(object arg)
{
//...
Ou vous pouvez faire ce que je préfère et créer une méthode générique:
public void DoSomething<T>(T arg)
{
//...
L'approche générique présente deux avantages majeurs, et je vais vous donner des exemples de leur utilité:
arg
, vous y avez toujours accès.Inversement, l'approche object
présente certains inconvénients importants:
arg
comme un object
, vous ne pourrez faire que ce que vous pourriez faire avec n'importe objet.object
, la variable sera encadré , ce qui signifie un impact sur les performances. Ce n'est pas un énorme succès, mais si vous appelez DoSomething
plusieurs milliers de fois de suite, vous pourriez commencer à le ressentir.L'ajout d'une contrainte de type à une méthode générique vous permet de restreindre la méthode afin qu'elle n'accepte que certains types. Pourquoi est-ce utile? Parce que même si vous ne savez pas - ou ne vous souciez pas - du type spécifique avec lequel vous travaillez, vous en savez maintenant quelque chose et vous pouvez utiliser ces informations.
Considérez la configuration suivante:
public interface IAnimal
{
void Move();
}
public class Duck : IAnimal
{
public void Move()
{
Console.WriteLine("Flying");
}
}
public class Fish : IAnimal
{
public void Move()
{
Console.WriteLine("Swimming");
}
}
public class Ant : IAnimal
{
public void Move()
{
Console.WriteLine("Walking");
}
}
Puisque nous avons une interface IAnimal
, nous pouvons écrire des méthodes génériques ciblant toute implémentation de IAnimal
:
public class Program
{
static void DoMove<T>(T animal) where T : IAnimal
{
animal.Move();
}
public static void Main(string[] args)
{
Duck duck = new Duck();
Fish fish = new Fish();
Ant ant = new Ant();
DoMove<Duck>(duck);
DoMove<Fish>(fish);
DoMove<Ant>(ant);
}
}
Exécutez-le: http://rextester.com/GOF1761
Lorsque nous écrivons la méthode DoMove
, peu nous importe que son paramètre animal
soit un Duck
, un Fish
, un Ant
ou autre chose. Tout ce qui nous intéresse, c'est d'appeler animal.Move()
. Puisque nous avons utilisé la contrainte where T : IAnimal
, Le compilateur sait tout ce que nous avons besoin de savoir:
animal
est de type T
.T
, il implémente IAnimal
.IAnimal
a une méthode Move()
.animal.Move()
.(Soit dit en passant, oui, nous pourrions simplement écrire DoMove
en tant que static void DoMove(IAnimal animal)
, mais c'est une autre discussion.)
Très bien, mais allons plus loin. Dans de nombreux cas, vous pouvez appeler des méthodes génériques sans avoir à spécifier leurs paramètres de type. Cela s'appelle inférence de type , et en plus de vous éviter de taper, cela peut être utile lorsque vous effectuez la même opération sur des objets de types différents.
public static void Main(string[] args)
{
IAnimal[] animals = new IAnimal[]
{
new Duck(),
new Fish(),
new Ant()
};
foreach (IAnimal animal in animals)
{
DoMove(animal);
}
}
Exécutez-le: http://rextester.com/OVKIA12317
Vous n'avez qu'à écrire la méthode DoMove<T>
Une seule fois, et vous pouvez l'appeler sur n'importe quel type de IAnimal
sans avoir à donner un type plus spécifique. La version appropriée de Move sera appelée à chaque fois, car DoMove<T>
Est en mesure de déduire le type à utiliser pour T
. Lorsque vous appelez DoMove(duck)
, .NET comprend que vous voulez vraiment dire DoMove<Duck>(duck)
, qui appelle ensuite la méthode Move
sur la classe Duck
.
Vous pouvez prendre object
comme type de paramètre. Encore mieux, peut-être, serait d'utiliser des génériques:
void MyMethod<T>(T parm) { ... }
De cette façon, le paramètre est en fait du type que l'utilisateur a transmis - il n'est pas encadré comme avec object
et les types de valeur.
void MyTestMethod<T>(T t) { }
vous donne une méthode de test générique, mais je ne peux imaginer aucun moyen qui pourrait être utile. De quoi avez-vous besoin pour tester? Comment savez-vous que le type T
possède ces méthodes? T
peut être de type any dans la méthode ci-dessus. Les seules méthodes que vous pouvez appeler à partir de t
dans l'exemple ci-dessus sont les méthodes courantes de object
.
Ce que vous devez vraiment faire, c'est identifier un comportement commun par rapport à un ou plusieurs types que vous souhaitez tester, et définir le contrat syntaxique de ce comportement via une interface. Vous pouvez ensuite contraindre votre méthode de test générique à n'accepter que les types qui implémentent cette interface.
interface IMyInterface
{
void DoSomething();
}
void MyTestMethod<T>(T t) where T : IMyInterface
{
t.DoSomething();
}
public void YourMethod<T>(T parameter)
{
}
essayez d'utiliser un mot clé dynamique, cela fonctionnera à condition que tous vos différents types aient les mêmes méthodes que celles utilisées par vos tests unitaires, sinon vous obtiendrez une exception d'exécution