A continuación, se combinan algunos de los mejores aspectos de varias otras respuestas, así como una técnica para permitir el aspecto clave de Cattener una Excrementpropiedad del RadioactivePootipo requerido , pero poder devolver eso simplemente como Poosi solo supiéramos que tenemos un en AnimalBaselugar de específicamente unCat .
No se requiere que los llamantes usen genéricos, a pesar de que estén presentes en las implementaciones, ni que llamen a una función con un nombre diferente para ser especial Poo.
La clase intermedia AnimalWithSpecialisationssolo sirve para sellar la Excrementpropiedad, conectándola a través de una SpecialPoopropiedad no pública a la clase derivada AnimalWithSpecialPoo<TPoo>que tiene unExcrement propiedad de un tipo de retorno derivado.
Si Cates el único animal que Pooes especial de alguna manera, o no queremos que el tipo de Excrementsea la característica definitoria principal de a Cat, la clase genérica intermedia podría omitirse en la jerarquía, por lo que se Catderiva directamente de AnimalWithSpecialisations, pero si hay son varios animales diferentes cuya característica principal es que Pooson especiales de alguna manera, separar la "placa de caldera" en clases intermedias ayuda a mantener elCat clase en sí bastante limpia, aunque a costa de un par de llamadas a funciones virtuales adicionales.
El código de ejemplo muestra que la mayoría de las operaciones esperadas funcionan "como se esperaba".
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;
}