Скажем, у вас есть метод, который делает:
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);
}
С различными реализациями используется. Один из них используется в:
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)
}
}
Теперь ничего особенного не удивительно. Мы не предполагаем конкретного выполнения IEnumerable. В самом деле, это будет работать для массивов и очень многих часто используемых коллекций (не могу думать об одном в System.Collections.Generic, который не совпадает с моей головой). Мы использовали только обычные методы и обычные методы расширения. Даже необычно иметь оптимизированный случай для коллекций с одним элементом. Мы могли бы, например, изменить список как массив, а может быть, HashSet (для автоматического удаления дубликатов) или LinkedList или несколько других вещей, и он будет продолжать работать.
Тем не менее, хотя мы не зависим от конкретной реализации, мы в зависимости от конкретной функции, в частности, от перемотки (Count()
либо вызовет ICollection.Count, либо перечислим через перечислимое число, .. проверка будет проходить
Кто-то, хотя видит результаты собственности и думает: «хмм, это немного расточительно» Они заменить его:
public IEnumerable<T> Results
{
get
{
using(IDataReader rdr = GetReader())
while(rdr.Read())
yield return Build(rdr);
}
}
это опять-таки вполне разумно, и действительно привести к значительным повышение производительности во многих случаях.Если CheckNames
не попадает в непосредственные «тесты», выполненные соответствующим кодером (возможно, он не попадает во множество путей кода), то тот факт, что CheckNames будет ошибочным (и, возможно, вернуть ложный результат в случае более одного имени, что может быть еще хуже, если оно открывает угрозу безопасности).
Любой юнит-тест, который попадает на CheckNames с более чем нулевыми результатами, собирается его поймать.
Между прочим сопоставимое (если более сложное) изменением является причиной для функции обратной совместимости в NPGSQL. Не так просто, как просто заменить List.Add() на доходность, но изменение способа работы ExecuteReader дало сопоставимое изменение от O (n) до O (1), чтобы получить первый результат. Однако до этого NpgsqlConnection позволяла пользователям получать другого читателя от соединения, в то время как первый был все еще открытым, а после этого не был. Документы для IDbConnection говорят, что вы не должны этого делать, но это не значит, что не было никакого исполняемого кода. К счастью, одним из таких фрагментов кода является тест NUnit, и добавлена возможность обратной совместимости, позволяющая продолжить работу такого кода только с изменением конфигурации.
Является ли этот вопрос языком агностиком? – kbrimington
@kbrimington C#. –
C# является предпочтительным, потому что это моя аудитория; Но я могу переписать его на C#, учитывая хороший, простой пример. – dgiard