Если вы используете C # 4.0, вы можете спросить себя, должен ли процессор возвращать IEnumerable<T>
вместо IList<T>
.Если ответ «да», то вы можете получить выгоду от ковариации:
abstract class AnimalProcessor {
public abstract IEnumerable<Animal> ProcessResults();
}
class GiraffeProcessor : AnimalProcessor {
public override IEnumerable<Animal> ProcessResults() {
return new List<Giraffe>();
}
}
class LionProcessor : AnimalProcessor {
public override IEnumerable<Animal> ProcessResults() {
return new List<Lion>();
}
}
У вас есть несколько преимуществ здесь.Во-первых, вы могли бы реализовать их как блоки итераторов:
class GiraffeProcessor : AnimalProcessor {
public override IEnumerable<Animal> ProcessResults() {
yield break;
}
}
Во-вторых, и менее просто, вы позволяете клиентскому коду решать, в какую коллекцию помещать животных - если таковые имеются.Например, предположим, что потребитель может захотеть LinkedList<Animal>
:
var animals = new LinkedList<Animal>(animalProcessor.ProcessResults());
. Или учтите, что клиенту может потребоваться только повторять последовательность:
foreach (var animal in animalProcessor.ProcessResults())
{ /*... do something ...*/ }
В любом случае, есливы использовали вызов ToList()
в ProcessResults, вы бы создавали список даром.Если потребитель действительно хочет List<Animal>
, это можно сделать очень легко:
var animals = new List<Animal>(animalProcessor.ProcessResults());
Наконец, вы также можете воспользоваться универсальным подходом, даже если вы измените тип интерфейса возвращаемого значения метода:
abstract class AnimalProcessor<T> where T : Animal {
public abstract IEnumerable<T> ProcessResults();
}
class GiraffeProcessor : AnimalProcessor<Giraffe> {
public override IEnumerable<Giraffe> ProcessResults() {
yield break;
}
}
class LionProcessor : AnimalProcessor<Lion> {
public override IEnumerable<Lion> ProcessResults() {
return Enumerable.Empty<Lion>();
}
}