Supposons que vous avez une méthode qui fait:
abstract class ProviderBase<T>
{
public IEnumerable<T> Results
{
get
{
List<T> list = new List<T>();
using(IDataReader rdr = GetReader())
while(rdr.Read())
list.Add(Build(rdr));
return list;
}
}
protected abstract IDataReader GetReader();
protected T Build(IDataReader rdr);
}
Avec diverses implémentations utilisées. L'un d'entre eux est utilisé dans:
public bool CheckNames(NameProvider source)
{
IEnumerable<string> names = source.Results;
switch(names.Count())
{
case 0:
return true;//obviously none invalid.
case 1:
//having one name to check is a common case and for some reason
//allows us some optimal approach compared to checking many.
return FastCheck(names.Single());
default:
return NormalCheck(names)
}
}
Maintenant, rien de tout cela n'est particulièrement bizarre. Nous n'assumons pas une implémentation particulière de IEnumerable. En effet, cela fonctionnera pour les tableaux et de très nombreuses collections couramment utilisées (je ne peux pas en trouver une dans System.Collections.Generic qui ne correspond pas au sommet de ma tête). Nous avons seulement utilisé les méthodes normales et les méthodes d'extension normales. Il n'est même pas inhabituel d'avoir un cas optimisé pour les collections à un seul article. Nous pourrions par exemple changer la liste en un tableau, ou peut-être un HashSet (pour supprimer automatiquement les doublons), ou une LinkedList ou quelques autres choses et ça va continuer à fonctionner. Néanmoins, bien que nous ne dépendions pas d'une implémentation particulière, nous dépendons d'une fonctionnalité particulière, en particulier celle d'être rembobinable (Count()
appellera ICollection.Count ou bien énumérera à travers l'énumérable, après quoi le nom- .. vérification aura lieu
Quelqu'un voit bien que les résultats bien et pense « hmm, c'est un peu inutile » Ils le remplacer par:
public IEnumerable<T> Results
{
get
{
using(IDataReader rdr = GetReader())
while(rdr.Read())
yield return Build(rdr);
}
}
Ce nouveau est tout à fait raisonnable, et conduira en effet à une considérable augmentation de la performance dans de nombreux cas.Si CheckNames
n'est pas touché dans les "tests" immédiats effectués par le codeur en question (peut-être qu'il n'est pas touché dans beaucoup de chemins de code), alors le fait que CheckNames va erreur (et peut renvoyer un faux résultat dans le cas de plus de 1 nom, ce qui peut être encore pire, s'il y a un risque de sécurité). Tout test unitaire qui frappe sur CheckNames avec des résultats supérieurs à zéro va le rattraper. Par ailleurs, un changement comparable (s'il est plus compliqué) est une raison pour une fonctionnalité de rétrocompatibilité dans NPGSQL. Pas aussi simple que de simplement remplacer List.Add() par un rendement de retour, mais une modification de la façon dont ExecuteReader a travaillé a donné un changement comparable de O (n) à O (1) pour obtenir le premier résultat. Cependant, avant cela, NpgsqlConnection permettait aux utilisateurs d'obtenir un autre lecteur à partir d'une connexion alors que le premier était toujours ouvert, et pas après. Les documents pour IDbConnection indiquent que vous ne devriez pas faire cela, mais cela ne veut pas dire qu'il n'y avait pas de code courant. Heureusement, un tel code était un test NUnit, et une fonction de rétrocompatibilité a été ajoutée pour permettre à ce code de continuer à fonctionner avec un simple changement de configuration.
Cette question est-elle agnostique? – kbrimington
@kbrimington C#. –
C# est préféré, parce que c'est mon public; Mais je peux le réécrire en C#, donné un bon exemple simple. – dgiard