Digamos que tienes un método que hace:
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);
}
Con varias implementaciones que se utilice. Uno de ellos se usa en:
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)
}
}
Ahora, nada de esto es particularmente extraño. No asumimos una implementación particular de IEnumerable. De hecho, esto funcionará para matrices y muchas colecciones de uso común (no se puede pensar en una en System.Collections.Generic que no coincida con la parte superior de mi cabeza). Solo hemos usado los métodos normales y los métodos de extensión normales. No es inusual tener un caso optimizado para colecciones de un solo artículo. Podríamos, por ejemplo, cambiar la lista para que sea una matriz, o tal vez un HashSet (para eliminar duplicados automáticamente), una LinkedList u otras cosas más y seguirá funcionando.
De todos modos, aunque no dependemos de una implementación en particular, dependemos de una función en particular, específicamente la de ser rebobinable (Count()
llamará a ICollection.Count o enumerará a través del enumerable, después de lo cual el nombre- .. comprobación se llevará a cabo
Alguien ve aunque los resultados propiedad y piensa "hmm, eso es un poco derrochador" Ellos reemplazan con:
public IEnumerable<T> Results
{
get
{
using(IDataReader rdr = GetReader())
while(rdr.Read())
yield return Build(rdr);
}
}
de nuevo, esto es perfectamente razonable, y de hecho va a llevar a una considerable aumento de rendimiento en muchos casos.Si CheckNames
no es golpeado en las "pruebas" inmediatas hechas por el codificador en cuestión (tal vez no se encuentre en muchas rutas de código), entonces el hecho de que CheckNames generará un error (y posiblemente arrojará un resultado falso en el caso) de más de 1 nombre, que puede ser incluso peor, si abre un riesgo de seguridad).
Sin embargo, cualquier prueba de unidad que llegue a CheckNames con más de cero resultados va a atraparla.
Incidentalmente una (si es más complicado) cambio comparable es una razón para una característica de compatibilidad hacia atrás en Npgsql. No es tan simple como simplemente reemplazar un List.Add() con un rendimiento de retorno, pero un cambio en la forma en que ExecuteReader funcionó dio un cambio comparable de O (n) a O (1) para obtener el primer resultado. Sin embargo, antes de eso, NpgsqlConnection permitía a los usuarios obtener otro lector de una conexión mientras que el primero todavía estaba abierto, y luego no. Los documentos para IDbConnection dicen que no deberías hacer esto, pero eso no significaba que no hubiera código de ejecución que sí lo hiciera. Afortunadamente, una de esas partes del código de ejecución fue una prueba NUnit y una característica de compatibilidad con versiones anteriores agregada para permitir que dicho código continúe funcionando con solo un cambio en la configuración.
¿Es esta pregunta independiente del idioma? – kbrimington
@kbrimington C#. –
C# es preferido, porque ese es el de mi audiencia; Pero puedo reescribirlo en C#, dado un buen ejemplo simple. – dgiard