Svi*_*ish 73 c# inheritance types overriding covariance
有没有办法覆盖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(); } }
}
Run Code Online (Sandbox Code Playgroud)
RadioactivePoo当然继承自Poo.
我想要这样做的原因是,那些使用Cat对象的人可以使用该Excrement属性而不必Poo投入RadioactivePoo,例如,Cat仍然可能是Animal列表的一部分,用户可能不一定知道或关心他们的放射性便便.希望有意义......
据我所知,编译器至少不允许这样做.所以我想这是不可能的.但是你会推荐什么作为解决方案呢?
Pao*_*sco 47
通用基类怎么样?
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> { }
Run Code Online (Sandbox Code Playgroud)
编辑:一种新的解决方案,使用扩展方法和标记界面......
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();
}
}
Run Code Online (Sandbox Code Playgroud)
这样Dog和Cat都继承自Animal(如评论中所述,我的第一个解决方案没有保留继承).
有必要使用标记接口明确标记类,这很痛苦,但也许这可以给你一些想法......
第二次编辑 @Svish:我修改了代码以明确地表明扩展方法没有以任何方式强制执行iPooProvider继承的事实BaseAnimal."更强烈的打字"是什么意思?
Dan*_*nas 31
这称为返回类型协方差,尽管有些人的意愿,但一般不支持C#或.NET .
我要做的是保持相同的签名,但ENSURE在派生类中添加一个附加子句,我确保这个子句返回一个RadioActivePoo.所以,简而言之,我是通过合同设计做的,我不能通过语法做.
其他人喜欢伪造它.我猜,没关系,但我倾向于节省"基础设施"代码行.如果代码的语义足够清楚,我很高兴,通过契约设计让我实现了这一点,尽管它不是编译时机制.
对于泛型也是如此,其他答案表明.我会使用它们的原因不仅仅是返回放射性便便 - 但这只是我.
rob*_*rob 19
我知道这个问题已经有很多解决方案,但我想我已经找到了解决现有解决方案问题的解决方案.
由于以下原因,我对一些现有解决方案不满意:
我的解决方案
这个解决方案应该通过使用泛型和方法隐藏来克服我上面提到的所有问题.
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> { }
Run Code Online (Sandbox Code Playgroud)
使用此解决方案,您无需覆盖Dog OR Cat中的任何内容!多么酷啊?以下是一些示例用法:
Cat bruce = new Cat();
IAnimal bruceAsAnimal = bruce as IAnimal;
Console.WriteLine(bruce.Excrement.ToString());
Console.WriteLine(bruceAsAnimal.Excrement.ToString());
Run Code Online (Sandbox Code Playgroud)
这将输出:"RadioactivePoo"两次,表明多态性尚未被破坏.
进一步阅读
MyType<Poo>从IAnimal返回并MyType<PooType>从BaseAnimal 返回的东西那么你需要使用它来能够在两者之间进行转换.还有这个选项(显式接口实现)
public class Cat:Animal
{
Poo Animal.Excrement { get { return Excrement; } }
public RadioactivePoo Excrement { get { return new RadioactivePoo(); } }
}
Run Code Online (Sandbox Code Playgroud)
你失去了使用基类来实现Cat的能力,但在正面,你保持了Cat和Dog之间的多态性.
但我怀疑增加的复杂性是值得的.