JamesD
JamesD

Reputation: 85

Using stronger typed lists in derived objects than the parent has defined

Info: IDogShelter and ICatShelter both inherit from IAnimalShelter.

Next, I have Cat and Dog classes which both implement and extend IAnimal.

IAnimalShelter has a List<IAnimal> called Animals.
ICatShelter has a List<Cat> called Animals.
IDogShelter has a List<Dog> called Animals.

Problem: ICatShelter can't have a List called Animals because IAnimalShelter has it defined as a List of IAnimal. VS says to use the "new" keyword. And so I just take it away instead and don't have a List of IAnimal on the IAnimalShelter.

And so - I have a method called GetAnimalShelters() which returns List of IAnimalShelter. I want then to be able to iterate through each IAnimal in the shelter, but can't because IAnimalShelter doesn't have a List of IAnimal. This leaves me having to cast my results into ICatShelter or IDogShelter in order to iterate through, but I'd like to be able to successfully add List of IAnimal to the IAnimalShelter instead. How can I do this?

For example:

public interface IAnimalShelter
{
    List<IAnimal> Animals { get; set; }
}

public interface ICatShelter
{
    List<Cat> Animals { get; set; }
}

public interface IDogShelter
{
    List<Dog> Animals { get; set; }
}

And then:

List<IAnimalShelter> shelters = GetShelters(search);  //Works
foreach (IAnimalShelter shelter in shelters)
{
    var x = shelter.Animals;  
}

So above, I want to just access the common parts of an animal, I want IAnimalShelter to have an Animals property, but for the derived shelter types to have more strongly typed Animals list.

Upvotes: 2

Views: 62

Answers (3)

JoaoFSA
JoaoFSA

Reputation: 267

Use generics:

public interface IAnimalShelter<T> where T : IAnimal
{
    List<T> Animals { get; set; }
}

public class Cat: IAnimal
{
}

public interface ICatShelter : IAnimalShelter<Cat>
{
}

public class CatShelter: ICatShelter
{
    public List<Cat> Animals { get; set; }
}

Didnt test it but this should do the trick, if your ICatShelter doesnt add anything to IAnimalShelter you can even drop it and derive CatShelter directly from IAnimalShelter

Upvotes: 0

CoderDennis
CoderDennis

Reputation: 13837

This is called Generics. The following definitions should give you what you're looking for:

public interface IAnimal { }

public class Cat : IAnimal { }

public class Dog : IAnimal { }

public interface IAnimalShelter<T> where T : IAnimal
{
    List<T> Animals { get; set; } 
}

public interface ICatShelter : IAnimalShelter<Cat> { }

public interface IDogShelter : IAnimalShelter<Dog> { }

The Animals property of an ICatShelter will be List<Cat>.

Implement the class like this and the compiler is happy:

public class CatShelter : ICatShelter
{
    public List<Cat> Animals { get; set; }
}

Upvotes: 0

supercat
supercat

Reputation: 81143

Your type IAnimalShelter is promising that its Animals property will yield an object whose Add method will accept a parameter of type Animal. If ICatShelter derives from IAnimalShelter, it must also have an Animals property that yields an object whose Add method will accept a parameter of type Animal.

To make this model work, you should define IShelter<out T> where T:Animal which includes an Animals property of type IReadOnlyList<T>, and a TryDonate(Animal it) method which will request that the shelter take in a particular animal. A CatShelter : IShelter<Cat> will be able to let client code receive a readable list of objects that are guaranteed to all be of type Cat, and clients may attempt to submit any kind of animal to it, though donations of types other than Cat will not get added to the list.

Upvotes: 1

Related Questions