Poniższe łączy niektóre z najlepszych aspektów kilku innych odpowiedzi, a także technikę umożliwiającą kluczowy aspekt Catposiadania Excrementwłaściwości wymaganego RadioactivePootypu, ale możliwość zwrócenia jej tak Poo, jakbyśmy tylko wiedzieli, że mamy AnimalBaseraczej 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 AnimalWithSpecialisationssłuży jedynie do zapieczętowania Excrementwłasności, łącząc ją poprzez niepubliczną SpecialPoowłaściwość z klasą pochodną, AnimalWithSpecialPoo<TPoo>która ma Excrementwłaściwość pochodnego typu zwracanego.
Jeśli Catjest to jedyne zwierzę, które Poojest w jakikolwiek sposób wyjątkowe, lub nie chcemy, aby typ Excrementbył główną cechą definiującą a Cat, pośrednia klasa ogólna może zostać pominięta w hierarchii, tak że Catpochodzi bezpośrednio z AnimalWithSpecialisations, ale jeśli istnieje istnieje kilka różnych zwierząt, których podstawową cechą jest to, że Poosą one w jakiś sposób wyjątkowe, rozdzielenie „kotłowni” na klasy pośrednie pomaga utrzymać Catsamą 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;
}