C# 为什么';继承不能按我认为应该的方式工作吗?
我有一些继承问题,因为我有一组相互关联的抽象类,这些抽象类需要一起重写以创建客户机实现。理想情况下,我想做如下工作:C# 为什么';继承不能按我认为应该的方式工作吗?,c#,oop,inheritance,covariance,contravariance,C#,Oop,Inheritance,Covariance,Contravariance,我有一些继承问题,因为我有一组相互关联的抽象类,这些抽象类需要一起重写以创建客户机实现。理想情况下,我想做如下工作: abstract class Animal { public Leg GetLeg() {...} } abstract class Leg { } class Dog : Animal { public override DogLeg Leg() {...} } class DogLeg : Leg { } abstract class Animal<Le
abstract class Animal
{
public Leg GetLeg() {...}
}
abstract class Leg { }
class Dog : Animal
{
public override DogLeg Leg() {...}
}
class DogLeg : Leg { }
abstract class Animal<LegType> where LegType : Leg
{
public abstract LegType GetLeg();
}
abstract class Leg { }
class Dog : Animal<DogLeg>
{
public override DogLeg GetLeg()
{
return new DogLeg();
}
}
class DogLeg : Leg { }
这将允许任何使用Dog类的人自动获得DogLegs,任何使用Animal类的人获得Legs。问题是被重写的函数必须与基类具有相同的类型,这样就不会编译。我不明白为什么它不应该,因为狗腿是隐式铸造到腿。我知道有很多方法可以解决这个问题,但我更好奇的是,为什么这在C#中不可能实现
编辑:我对其进行了一些修改,因为我实际上在代码中使用的是属性而不是函数
编辑:我把它改回了函数,因为答案只适用于那种情况(属性的set函数的value参数的协方差不应该起作用)。对不起,波动太大了!我意识到这会让很多答案看起来不相关。GetLeg()必须返回Leg才能作为覆盖。但是,您的Dog类仍然可以返回DogLeg对象,因为它们是Leg的子类。然后,客户可以像狗腿一样对它们施放和操作
public class ClientObj{
public void doStuff(){
Animal a=getAnimal();
if(a is Dog){
DogLeg dl = (DogLeg)a.GetLeg();
}
}
}
作为返回类型,Dog应该返回一条腿,而不是一条狗腿。实际的类可能是DogLeg,但关键是要解耦,这样Dog的用户就不必知道DogLegs,他们只需要知道Legs 更改:
class Dog : Animal
{
public override DogLeg GetLeg() {...}
}
致:
不要这样做:
if(a instanceof Dog){
DogLeg dl = (DogLeg)a.GetLeg();
它违背了抽象类型编程的目的
隐藏DogLeg的原因是因为抽象类中的GetLeg函数返回一个抽象Leg。如果要覆盖GetLeg,则必须返回一个Leg。这就是在抽象类中使用方法的意义所在。将该方法传播给它的孩子。如果您想让狗的用户了解狗腿,请创建一个名为GetDogLeg的方法并返回一个DogLeg
如果你能按照提问者的要求去做,那么每一个使用动物的人都需要了解所有的动物。是的,我知道我可以投,但这意味着客户必须知道狗有狗腿。我想知道的是,考虑到隐式转换的存在,是否有技术上的原因导致这不可能实现。简单的回答是GetLeg的返回类型是不变的。答案很长,可以在这里找到: 我想补充一点,虽然继承通常是大多数开发人员从工具箱中取出的第一个抽象工具,但几乎总是可以使用组合来代替。对于API开发人员来说,合成的工作稍微多一些,但会使API对其用户更有用。@Brian Leahy
显然,如果你只是作为一条腿进行手术,就没有必要也没有理由施放石膏。但是,如果有狗腿或狗狗特有的行为,有时也会有必要使用石膏的原因。导致问题的概念在@Luke中描述 我想你可能误解了遗产。GetLeg()将返回一个DogLeg对象
public class Dog{
public Leg GetLeg(){
DogLeg dl = new DogLeg(super.GetLeg());
//set dogleg specific properties
}
}
Animal a = getDog();
Leg l = a.GetLeg();
l.kick();
实际调用的方法将是Dog.GetLeg();和DogLeg.Kick()(我假设存在一个方法Leg.Kick()),声明的返回类型为DogLeg是不必要的,因为这就是返回的,即使Dog.GetLeg()的返回类型是Leg。您也可以返回Leg和/或DogLeg都实现的接口ILeg
abstract class Animal
{
public virtual Leg GetLeg ()
}
abstract class Leg { }
class Dog : Animal
{
public override Leg GetLeg () { return new DogLeg(); }
}
class DogLeg : Leg { void Hump(); }
这样做,您就可以利用客户机中的抽象:
Leg myleg = myDog.GetLeg();
然后,如果需要,您可以施放:
if (myleg is DogLeg) { ((DogLeg)myLeg).Hump()); }
完全是人为的,但关键是你可以这样做:
foreach (Animal a in animals)
{
a.GetLeg().SomeMethodThatIsOnAllLegs();
}
同时仍然保留在狗腿上使用特殊驼峰方法的能力。也许通过一个例子更容易看出问题:
Animal dog = new Dog();
dog.SetLeg(new CatLeg());
现在,如果你是狗编译的话,应该可以编译了,但我们可能不想要这样一个变种
一个相关的问题是Dog[]应该是Animal[],还是IList应该是IList?需要记住的重要一点是,您可以在每个使用基类型的地方使用派生类型(您可以将Dog传递给任何需要Animal的方法/属性/字段/变量) 让我们来看看这个函数:
public void AddLeg(Animal a)
{
a.Leg = new Leg();
}
一个完全有效的函数,现在让我们这样调用函数:
AddLeg(new Dog());
如果Dog.Leg属性不是Leg类型,AddLeg函数突然包含错误,无法编译。这并不是说它有多大用处,但值得注意的是,Java确实支持协变返回,因此这正是您所希望的。很明显,Java没有属性;) 很明显,如果你是
在断了的狗腿上做手术 您可以使用泛型和接口在C#中实现这一点:
抽象类Leg{}
接口IAnimal{Leg GetLeg();}
抽象类动物:IAnimal其中TLeg:腿
{public abstract TLeg GetLeg();
Leg IAnimal.GetLeg(){返回this.GetLeg();}
}
狗类:动物
{公共类狗腿:狗腿{}
public override DogLeg GetLeg(){return new DogLeg();}
}
让重写方法的签名具有作为重写方法(phew)中的返回类型的子类型的返回类型是完全正确的愿望。毕竟,它们是运行时类型兼容的
<>但是C++在“重写”方法中还没有支持“协变返回类型”(不像C++(1998)和java(2004))。
正如Eric Lippert在文章中所说的那样,你需要在可预见的未来努力工作
[2008年6月19日]:
这种方差称为“返回型协方差”
我们没有计划在C#中实施这种差异
通过使用具有适当约束的泛型,可以实现所需的功能,如以下所示:
abstract class Animal
{
public Leg GetLeg() {...}
}
abstract class Leg { }
class Dog : Animal
{
public override DogLeg Leg() {...}
}
class DogLeg : Leg { }
abstract class Animal<LegType> where LegType : Leg
{
public abstract LegType GetLeg();
}
abstract class Leg { }
class Dog : Animal<DogLeg>
{
public override DogLeg GetLeg()
{
return new DogLeg();
}
}
class DogLeg : Leg { }
抽象类动物,其中LegType:Leg
{
公共抽象LegType GetLeg();
}
抽象类Leg{}
狗类:动物
{
公共优先权
abstract class Leg { }
class DogLeg : Leg { }
interface IAnimal
{
Leg GetLeg();
}
class Dog : IAnimal
{
public override DogLeg GetLeg() { /* */ }
Leg IAnimal.GetLeg() { return GetLeg(); }
}