Relationship between arrays in a C # general list

I have an example where I want the abstract class interface to return something like this

abstract class AnimalProcessor { public abstract IList<Animal> ProcessResults(); } 

Then 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 specific classes must have the same signature to override the ProcessResults() method, so they need to return IList<Animal> , however the ACTUAL data I want to return is IList<Lion> , IList<Giraffe> etc., but then the call code should execute

 GiraffeProcessor processor = new GiraffeProcessor(); IList<Animal> results = processor.GetResults(); 

Which does not give me the Ilist I want.

Problems

1) The above code does not compile. GiraffeProcessor should return a specific List<Animal> , you can fill it with Giraffe objects, but the type of object you create to return should be List<Animal> . Not ideal.

2) When you return the results, you can only get IList<Animal> , not IList<Giraffe> . I tried explicitly casting IList<Giraffe> with IList<Giraffe> results = (IList<Giraffe>) processor.GetResults(); which gives a runtime error, presumably because the returned object is NOT an IList<Giraffe> , it is an IList<Animal> that contains CONTIINS Giraffe objects.

Can anyone suggest what I am doing wrong here, with my design, since Im a bit of a dead end regarding the best way to accomplish this.

+4
source share
3 answers

What 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>(); } } 
+6
source

This can be solved by declaring AnimalProcessor with a common type of constraint, for example.

 public abstract class AnimalProcessor<T> where T : Animal { public abstract IList<T> ProcessResults(); } 

If this does not work, you can use the LINQ Cast statement, for example:

 public class GiraffeProcessor : AnimalProcessor { public override IList<Animal> ProcessResults() { return new List<Giraffe>().Cast<Animal>(); } } 

Or save the list inside as Animal, but add Giraffe to it, for example.

 public class GiraffeProcessor : AnimalProcessor { private List<Giraffe> _innerList = new List<Giraffe>(); public override IList<Animal> ProcessResults() { return new List<Animal>(innerList ); } } 

Yours faithfully,

+1
source

If you are using C # 4.0, you can ask yourself if the processor should return an IEnumerable<T> , not an 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 several advantages here. First, you can implement them as iterator blocks:

 class GiraffeProcessor : AnimalProcessor { public override IEnumerable<Animal> ProcessResults() { yield break; } } 

Secondly, and less trivially, you allow the client code to decide what collection to collect for dropping animals - if any. For example, think that a consumer might wish a LinkedList<Animal> :

 var animals = new LinkedList<Animal>(animalProcessor.ProcessResults()); 

Or think that the client may only need to repeat the sequence:

 foreach (var animal in animalProcessor.ProcessResults()) { /*... do something ...*/ } 

In any case, if you used the ToList() call in ProcessResults, you created a list for nothing. If the consumer really wants a List<Animal> , this can be done very easily:

 var animals = new List<Animal>(animalProcessor.ProcessResults()); 

Finally, you can also take the general approach, even if you change the interface type of the method 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>(); } } 
+1
source

Source: https://habr.com/ru/post/1389259/


All Articles