是没办法复盖的回报类型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 酒店没有必要投的 PooRadioactivePoo 同时,例如 Cat 可能仍然是一部分 Animal 名单,用户可能不一定知道或关心自己的放射性便便。希望那感觉...

尽我所能看到的编译器,不允许这样最少。因此我猜测这是不可能的。但是你有什么建议作为解决这个?

有帮助吗?

解决方案

什么一个通用的基类?

public class Poo { }
public class RadioactivePoo : Poo { }

public class BaseAnimal<PooType> 
    where PooType : Poo, new() {
    PooType Excrement {
        get { return new PooType(); }
    }
}

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

编辑:一个新的解决方案,使用扩展的方法和标记的接口...

public class Poo { }
public class RadioactivePoo : Poo { }

// just a marker interface, to get the poo type
public interface IPooProvider<PooType> { }

// Extension method to get the correct type of excrement
public static class IPooProviderExtension {
    public static PooType StronglyTypedExcrement<PooType>(
        this IPooProvider<PooType> iPooProvider) 
        where PooType : Poo {
        BaseAnimal animal = iPooProvider as BaseAnimal;
        if (null == animal) {
            throw new InvalidArgumentException("iPooProvider must be a BaseAnimal.");
        }
        return (PooType)animal.Excrement;
    }
}

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

public class Dog : BaseAnimal, IPooProvider<Poo> { }

public class Cat : BaseAnimal, IPooProvider<RadioactivePoo> {
    public override Poo 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.StronglyTypedExcrement();
    }
}

这样狗和猫的两个继承自动物的(如指出的评论,我的第一个解决方案没有保留的继承).
它必须标明确的类与标记的接口,这是痛苦的,但这也许可以给你一些想法...

第二编辑 @Svish:我修改的代码显示explitly扩展的方法不是强制执行以任何方式的事实, iPooProvider 继承 BaseAnimal.你是什么意思,"甚至更强烈的归类"?

其他提示

这就是所谓的返回类型的协方差,并在C#或.NET一般不支持,尽管有些人的的希望

我会做的就是保持相同的签名,但增加一个额外的ENSURE条款派生类中,我保证这一个返回RadioActivePoo。因此,简而言之,我会通过做设计合同什么,我无法通过语法做。

其他人则倾向于该代替。这没关系,我想,但我倾向于节约守则“基础设施”行。如果代码的语义是很清楚,我很高兴,和契约式设计让我做到这一点,虽然它不是一个编译时的机制。

同样的仿制药,其其他的答案建议。我会用他们的不仅仅是返回放射性便便更好的理由 - 不过这只是我的

我知道有很多解决方案,这个问题已经是但我认为我已经想出了一个修正的问题,我不得不与现有的解决方案。

我是不是快乐的一些现有的解决方案,原因如下:

  • 保罗,第一个解决方案: 猫和狗没有一个共同的基类。
  • 保罗,第二方案: 是有点复杂和难以阅读。
  • 丹尼尔Daranas的解决方案: 这一工作,但它会扰乱你的代码有很多不必要的铸造和调试。Assert()发言。
  • hjb417的方案: 这个解决方案不会让你保持你的逻辑中的一个基类。其中的逻辑是很微不足道,在这个例子(调用的一个构造),但在真实世界的例子,它不是。

我的解决方案

这个解决方案应该克服的所有问题,我在上面提到通过使用两型和方法躲藏。

public class Poo { }
public class RadioactivePoo : Poo { }

interface IAnimal
{
    Poo Excrement { get; }
}

public class BaseAnimal<PooType> : IAnimal
    where PooType : Poo, new()
{
    Poo IAnimal.Excrement { get { return (Poo)this.Excrement; } }

    public PooType Excrement
    {
        get { return new PooType(); }
    }
}

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

这个解决方案不需要重写什么狗或猫!这里是一些样品用途:

Cat bruce = new Cat();
IAnimal bruceAsAnimal = bruce as IAnimal;
Console.WriteLine(bruce.Excrement.ToString());
Console.WriteLine(bruceAsAnimal.Excrement.ToString());

这将产出:"RadioactivePoo"的两倍,这表明,多没有被打破。

进一步阅读

  • 明确的界面执行情况
  • 新的修改.我没有利用它在这个简化的解决办法,但可能需要在一个更复杂的解决方案。例如,如果你想创造一个接口BaseAnimal然后你会需要用它在你的decleration的"PooType排泄物".
  • 出通用的改性剂(协).我再没有使用它在这个解决方案,但如果你想做些什么样的回报 MyType<Poo> 从IAnimal和返回 MyType<PooType> 从BaseAnimal然后你会需要使用它能够投两者之间的关系。

还有该选项(显式接口的实现)

public class Cat:Animal
{
  Poo Animal.Excrement { get { return Excrement; } }
  public RadioactivePoo Excrement { get { return new RadioactivePoo(); } }
}

您失去使用基类来实现猫的能力,但在正侧,你把猫与狗之间的多态性。

但我怀疑增加的复杂性是值得的。

为什么不定义创建“排泄物”一个受保护的虚拟方法,并保持它返回“粪”非虚拟的公共财产。然后派生类可以覆盖基类的返回类型。

在下面的例子中,我使“排泄物”非虚拟但提供属性ExcrementImpl以允许派生类来提供合适的“浦”。然后派生类型可以通过隐藏基类实现重写“粪便”的返回类型。

E.x:

namepace ConsoleApplication8

{
public class Poo { }

public class RadioactivePoo : Poo { }

public interface Animal
{
    Poo Excrement { get; }
}

public class AnimalBase
{
    public Poo Excrement { get { return ExcrementImpl; } }

    protected virtual Poo ExcrementImpl
    {
        get { return new Poo(); }
    }
}

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

public class Cat : AnimalBase
{
    protected override Poo ExcrementImpl
    {
        get { return new RadioactivePoo(); }
    }

    public new RadioactivePoo Excrement { get { return (RadioactivePoo)ExcrementImpl; } }
}
}
如果我错了

纠正我,但是,这不是pollymorphism的整点能够返回RadioActivePoo如果从便便继承,合同将是相同的抽象类,但只返回RadioActivePoo()

尝试这种情况:

namespace ClassLibrary1
{
    public interface Animal
    {   
        Poo Excrement { get; }
    }

    public class Poo
    {
    }

    public class RadioactivePoo
    {
    }

    public class AnimalBase<T>
    {   
        public virtual T Excrement
        { 
            get { return default(T); } 
        }
    }


    public class Dog : AnimalBase<Poo>
    {  
        // No override, just return normal poo like normal animal
    }

    public class Cat : AnimalBase<RadioactivePoo>
    {  
        public override RadioactivePoo Excrement 
        {
            get { return new RadioactivePoo(); } 
        }
    }
}

我想我已经找到了不依赖于仿制药或扩展方法,而是方法隐藏的方式。它可以打破多态性,但是,所以要特别小心,如果你远离猫继承。

我希望这篇文章仍然可以帮助别人,尽管为8月中下旬。

public interface Animal
{
    Poo Excrement { get; }
}

public class Poo
{
}

public class RadioActivePoo : Poo
{
}

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

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

public class CatBase : AnimalBase
{
    public override Poo Excrement { get { return new RadioActivePoo(); } }
}

public class Cat : CatBase
{
    public new RadioActivePoo Excrement { get { return (RadioActivePoo) base.Excrement; } }
}

这可能有助于如果RadioactivePoo从大便衍生,然后使用泛型。

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

我相信你的答案被称为协方差。

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();
    }
}

您可以只使用一个返回的接口。在你的情况,IPOO。

此,优选使用通用类型,在你的情况下,由于使用的是评论基类。

好了,它实际上是可能返回一个具体类型从继承返回类型(甚至对于静态方法)而变化,这要归功于dynamic

public abstract class DynamicBaseClass
{
    public static dynamic Get (int id) { throw new NotImplementedException(); }
}

public abstract class BaseClass : DynamicBaseClass
{
    public static new BaseClass Get (int id) { return new BaseClass(id); }
}

public abstract class DefinitiveClass : BaseClass
{
    public static new DefinitiveClass Get (int id) { return new DefinitiveClass(id);
}

public class Test
{
    public static void Main()
    {
        var testBase = BaseClass.Get(5);
        // No cast required, IntelliSense will even tell you
        // that var is of type DefinitiveClass
        var testDefinitive = DefinitiveClass.Get(10);
    }
}

我在API包装我写了我公司实施此。如果您打算开发一个API,这个有潜力提高某些用例的易用性和开发经验。尽管如此,dynamic的使用具有的性能产生影响,因此要尽量避免。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top