Reputation: 5055
I have an example where I want an abstract class interface to return something like this
abstract class AnimalProcessor {
public abstract IList<Animal> ProcessResults();
}
Then the concrete examples
class GiraffeProcessor : AnimalProcessor {
public override IList<Animal> ProcessResults() {
return new List<Giraffe>();
}
}
class LionProcessor : AnimalProcessor {
public override IList<Animal> ProcessResults() {
return new List<Lion>();
}
}
The problem is that the concrete classes need to have the same signature to override the ProcessResults()
method so they need to return an IList<Animal>
, however the ACTUAL data I want to return is an IList<Lion>
, IList<Giraffe>
etc, but then the calling code has to do
GiraffeProcessor processor = new GiraffeProcessor();
IList<Animal> results = processor.GetResults();
Which does not give me an Ilist which is what I want.
Problems
1) Above code does not compile. The giraffeProcessor has to return a concrete List<Animal>
, you can populate it with Giraffe
objects but the object type you construct to return has to be List<Animal>
. Not ideal.
2) When you return the results, you can only get an IList<Animal>
, not IList<Giraffe>
. I have tried casting explicitly to IList<Giraffe>
with
IList<Giraffe> results = (IList<Giraffe>) processor.GetResults();
which gives a runtime error, presumably because the object returned is NOT an IList<Giraffe>
, it is an IList<Animal>
which CONTAINS Giraffe
objects.
Can anyone suggest what I am doing wrong here with my design as Im a bit stumped as to the best way to accomplish this.
Upvotes: 4
Views: 230
Reputation: 43056
If you are using C# 4.0, you can ask yourself whether the processor should return IEnumerable<T>
rather than IList<T>
. If the answer is "yes", then you can profit from covariance:
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>();
}
}
You have a couple of advantages here. First, you could implement these as iterator blocks:
class GiraffeProcessor : AnimalProcessor {
public override IEnumerable<Animal> ProcessResults() {
yield break;
}
}
Second, and less trivially, you allow the client code to decide what kind of collection to dump the animals into -- if any. For example, consider that the consumer might want a LinkedList<Animal>
:
var animals = new LinkedList<Animal>(animalProcessor.ProcessResults());
Or consider that the client might need only to iterate the sequence:
foreach (var animal in animalProcessor.ProcessResults())
{ /*... do something ...*/ }
In either case, if you were using a ToList()
call in ProcessResults, you'd be creating a list for nothing. If the consumer really wants a List<Animal>
, that can be accomplished very easily:
var animals = new List<Animal>(animalProcessor.ProcessResults());
Finally, you can also benefit from the generic approach, even if you change the interface type of the method's return value:
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>();
}
}
Upvotes: 1
Reputation: 21541
You could resolve this by declaring AnimalProcessor with a generic type constraint, e.g.
public abstract class AnimalProcessor<T> where T : Animal
{
public abstract IList<T> ProcessResults();
}
If that doesnt work, you could use the LINQ Cast operator, for example:
public class GiraffeProcessor : AnimalProcessor
{
public override IList<Animal> ProcessResults()
{
return new List<Giraffe>().Cast<Animal>();
}
}
Or, store the list internally as Animal but add Giraffe's to it, e.g.
public class GiraffeProcessor : AnimalProcessor
{
private List<Giraffe> _innerList = new List<Giraffe>();
public override IList<Animal> ProcessResults()
{
return new List<Animal>(innerList ); }
}
Best regards,
Upvotes: 1
Reputation: 1063358
How about:
abstract class AnimalProcessor<T> where T : Animal {
public abstract IList<T> ProcessResults();
}
class GiraffeProcessor : AnimalProcessor<Giraffe> {
public override IList<Giraffe> ProcessResults() {
return new List<Giraffe>();
}
}
class LionProcessor : AnimalProcessor<Lion> {
public override IList<Lion> ProcessResults() {
return new List<Lion>();
}
}
Upvotes: 6