C #: переопределение типов возврата - PullRequest
73 голосов
/ 26 июня 2009

Есть ли способ переопределить типы возврата в C #? Если да, то как, а если нет, то почему и как это рекомендуется делать?

В моем случае у меня есть интерфейс с абстрактным базовым классом и его потомками. Я хотел бы сделать это (хорошо, не совсем, но в качестве примера!):

public interface Animal
{
   Poo Excrement { get; }
}

public class AnimalBase
{
   public virtual Poo Excrement { get { return new Poo(); } }
}

public class Dog
{
  // No override, just return normal poo like normal animal
}

public class Cat
{
  public override RadioactivePoo Excrement { get { return new RadioActivePoo(); } }
}

RadioactivePoo конечно, наследуется от Poo.

Моя причина для этого заключается в том, чтобы те, кто использует объекты Cat, могли использовать свойство Excrement, не приводя Poo к RadioactivePoo, в то время как, например, Cat все еще может быть частью Animal список, в котором пользователи могут не знать или заботиться о своем радиоактивном состоянии. Надеюсь, что это имеет смысл ...

Насколько я вижу, компилятор не позволяет этого по крайней мере. Так что я думаю, что это невозможно. Но что бы вы порекомендовали в качестве решения этой проблемы?

Ответы [ 13 ]

0 голосов
/ 28 марта 2013

Мне кажется, ваш ответ называется ковариацией.

class Program
{
    public class Poo
    {
        public virtual string Name { get{ return "Poo"; } }
    }

    public class RadioactivePoo : Poo
    {
        public override string Name { get { return "RadioactivePoo"; } }
        public string DecayPeriod { get { return "Long time"; } }
    }

    public interface IAnimal<out T> where T : Poo
    {
        T Excrement { get; }
    }

    public class Animal<T>:IAnimal<T> where T : Poo 
    {
        public T Excrement { get { return _excrement ?? (_excrement = (T) Activator.CreateInstance(typeof (T), new object[] {})); } } 
        private T _excrement;
    }

    public class Dog : Animal<Poo>{}
    public class Cat : Animal<RadioactivePoo>{}

    static void Main(string[] args)
    {
        var dog = new Dog();
        var cat = new Cat();

        IAnimal<Poo> animal1 = dog;
        IAnimal<Poo> animal2 = cat;

        Poo dogPoo = dog.Excrement;
        //RadioactivePoo dogPoo2 = dog.Excrement; // Error, dog poo is not RadioactivePoo.

        Poo catPoo = cat.Excrement;
        RadioactivePoo catPoo2 = cat.Excrement;

        Poo animal1Poo = animal1.Excrement;
        Poo animal2Poo = animal2.Excrement;
        //RadioactivePoo animal2RadioactivePoo = animal2.Excrement; // Error, IAnimal<Poo> reference do not know better.


        Console.WriteLine("Dog poo name: {0}",dogPoo.Name);
        Console.WriteLine("Cat poo name: {0}, decay period: {1}" ,catPoo.Name, catPoo2.DecayPeriod);
        Console.WriteLine("Press any key");

        var key = Console.ReadKey();
    }
}
0 голосов
/ 19 декабря 2012

FYI. Это легко реализуется в Scala.

trait Path

trait Resource
{
    def copyTo(p: Path): Resource
}
class File extends Resource
{
    override def copyTo(p: Path): File = new File
    override def toString = "File"
}
class Directory extends Resource
{
    override def copyTo(p: Path): Directory = new Directory
    override def toString = "Directory"
}

val test: Resource = new Directory()
test.copyTo(null)

Вот живой пример, с которым вы можете играть: http://www.scalakata.com/50d0d6e7e4b0a825d655e832

0 голосов
/ 26 июня 2009

Может помочь, если RadioactivePoo получен из poo, а затем использовать обобщенные значения.

...