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 Cat
tener una Excrement
propiedad del RadioactivePoo
tipo requerido , pero poder devolver eso simplemente como Poo
si solo supiéramos que tenemos un en AnimalBase
lugar 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 AnimalWithSpecialisations
solo sirve para sellar la Excrement
propiedad, conectándola a través de una SpecialPoo
propiedad no pública a la clase derivada AnimalWithSpecialPoo<TPoo>
que tiene unExcrement
propiedad de un tipo de retorno derivado.
Si Cat
es el único animal que Poo
es especial de alguna manera, o no queremos que el tipo de Excrement
sea la característica definitoria principal de a Cat
, la clase genérica intermedia podría omitirse en la jerarquía, por lo que se Cat
deriva directamente de AnimalWithSpecialisations
, pero si hay son varios animales diferentes cuya característica principal es que Poo
son 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;
}