Poniższe łączy niektóre z najlepszych aspektów kilku innych odpowiedzi, a także technikę umożliwiającą kluczowy aspekt Cat
posiadania Excrement
właściwości wymaganego RadioactivePoo
typu, ale możliwość zwrócenia jej tak Poo
, jakbyśmy tylko wiedzieli, że mamy AnimalBase
raczej niż konkretnie aCat
.
Obiekt wywołujący nie musi używać typów ogólnych, mimo że są one obecne w implementacjach, ani wywoływać funkcji o innych nazwach w celu uzyskania specjalnego Poo
.
Klasa pośrednia AnimalWithSpecialisations
służy jedynie do zapieczętowania Excrement
własności, łącząc ją poprzez niepubliczną SpecialPoo
właściwość z klasą pochodną, AnimalWithSpecialPoo<TPoo>
która ma Excrement
właściwość pochodnego typu zwracanego.
Jeśli Cat
jest to jedyne zwierzę, które Poo
jest w jakikolwiek sposób wyjątkowe, lub nie chcemy, aby typ Excrement
był główną cechą definiującą a Cat
, pośrednia klasa ogólna może zostać pominięta w hierarchii, tak że Cat
pochodzi bezpośrednio z AnimalWithSpecialisations
, ale jeśli istnieje istnieje kilka różnych zwierząt, których podstawową cechą jest to, że Poo
są one w jakiś sposób wyjątkowe, rozdzielenie „kotłowni” na klasy pośrednie pomaga utrzymać Cat
samą klasę w dość czystym stanie, aczkolwiek kosztem kilku dodatkowych wywołań funkcji wirtualnych.
Przykładowy kod pokazuje, że większość oczekiwanych operacji działa „zgodnie z oczekiwaniami”.
public interface IExcretePoo<out TPoo>
where TPoo : Poo
{
TPoo Excrement { get; }
}
public class Poo
{ }
public class RadioactivePoo : Poo
{ }
public class AnimalBase : IExcretePoo<Poo>
{
public virtual Poo Excrement { get { return new Poo(); } }
}
public class Dog : AnimalBase
{
}
public abstract class AnimalWithSpecialisations : AnimalBase
{
public sealed override Poo Excrement { get { return SpecialPoo; } }
protected virtual Poo SpecialPoo { get { return base.Excrement; } }
}
public abstract class AnimalWithSpecialPoo<TPoo> : AnimalWithSpecialisations, IExcretePoo<TPoo>
where TPoo : Poo
{
sealed protected override Poo SpecialPoo { get { return Excrement; } }
public new abstract TPoo Excrement { get; }
}
public class Cat : AnimalWithSpecialPoo<RadioactivePoo>
{
public override RadioactivePoo Excrement { get { return new RadioactivePoo(); } }
}
class Program
{
static void Main(string[] args)
{
Dog dog = new Dog();
Poo dogPoo = dog.Excrement;
Cat cat = new Cat();
RadioactivePoo catPoo = cat.Excrement;
AnimalBase animal = cat;
Poo animalPoo = catPoo;
animalPoo = animal.Excrement;
AnimalWithSpecialPoo<RadioactivePoo> radioactivePooingAnimal = cat;
RadioactivePoo radioactivePoo = radioactivePooingAnimal.Excrement;
IExcretePoo<Poo> pooExcreter = cat;
IExcretePoo<RadioactivePoo> radioactivePooExcreter = cat;
animal = dog;
animalPoo = dogPoo;
pooExcreter = dog;
}