Далі поєднуються деякі найкращі аспекти кількох інших відповідей, а також техніка, яка дозволяє визначити ключовий аспект Catнаявності Excrementвластивості необхідного RadioactivePooтипу, але можливості повернути це як просто, Pooякщо ми лише знаємо, що маємо, AnimalBaseа не зокрема aCat .
Від абонентів не вимагається використовувати загальні засоби, хоча вони присутні в реалізаціях, а також не викликати інакше названу функцію для отримання спеціальних Poo .
Проміжний клас AnimalWithSpecialisationsслужить лише для герметизації Excrementвластивості, з'єднуючи його через непублічну SpecialPooвластивість з похідним класом, AnimalWithSpecialPoo<TPoo>який маєExcrement властивість похідного типу повернення.
Якщо Catє єдиною твариною, Pooяка будь-якою особливою, або ми не хочемо, щоб тип Excrementбув головною визначальною ознакою a Cat, проміжний загальний клас може бути пропущений в ієрархії, що Catпоходить безпосередньо з AnimalWithSpecialisations, але якщо є - це кілька різних тварин, основною характеристикою яких є те, що вони Pooє якимось особливим чином, поділ "шаблону" на проміжні класи допомагає зберегтиCat клас досить чистим, хоча і ціною пари додаткових викликів віртуальних функцій.
Приклад коду показує, що більшість очікуваних операцій працюють "як очікувалося".
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;
}