在覆盖子类的函数中使用子接口



尊敬的OOP专家,

如果之前有人问过这个问题,我很抱歉,我还没有找到任何类似的问题(我可能没有合适的词语来解释(。

为了制作一个灵活的系统,我尝试使用InterfacesAbstract类。所涉及的实体非常简单:

public interface IAbilityTarget
{
// A bunch of properties and functions
}
public interface IDamagable : IAbilityTarget
{
int Life { get; set; }
}
public abstract class Ability
{
public abstract bool CanBeUsed( IAbilityTarget[] targets );
public abstract void Use( IAbilityTarget[] targets );
}    
public class Fireball : Ability
{
public override bool CanBeUsed( IDamagable[] targets )
{
return true ; // for the sake of the example
}
public override void Use( IDamagable[] targets )
{
for( int index = 0 ; index < targets.Length ; ++index )
targets[index].Life -= 1 ;
}
}

我的问题如下:为什么我有CS0115错误?

错误CS0115:"Fireball.CanBeUsed(IDamagable[]("标记为覆盖,但未找到覆盖的合适方法

由于IDamagable扩展了IAbilityTarget,我不明白为什么会出现错误。我不想在Fireball中实现以下内容来避免错误:

public override bool CanBeUsed( IAbilityTarget[] targets )
{
return true;
}
public bool CanBeUsed( IDamagable[] targets )
{
return CanBeUsed( (IAbilityTarget[]) targets );
}

为了避免错误,我必须进行哪些更改,而不必为每个子接口过载CanBeUsed

您是Fireball违反了Liskov替换原则。

因为引用Ability的客户端可以将IAbilityTarget的数组传递给它的每个方法。

但是,如果该引用的底层(运行时(类型真的是Fireball,那么传递给方法的那些对象必须实现IDamagable,这是一个更严格的要求。例如,如果不改变方法的调用方式,就不能用Fireball代替Ability

通常,重写在所需的先决条件方面必须是less严格的(不使用类型,而是使用传递对象的任何状态(。你的限制性更强了。

由于IDamagable扩展了IAbilityTarget,我不明白为什么会出现错误。

如果Fireball中的Use方法确实覆盖了Ability中的Use方法,则会出现不一致的情况。

class MyDummyTarget : IAbilityTarget { ... }
...
Ability myAbility = new Fireball();
myAbility.Use(new IAbilityTarget[] { new MyDummyTarget() });

从编译器的角度来看,这应该有效:myAbility.Use需要一个IAbilityTarget[],而你已经给了它。然而,由于重写,myAbility.Use调用了Fireball中定义的方法,该方法接受IDamagable[]。现在,如何将数组中的MyDummyTarget对象转换为IDamagable?没有办法。

您需要更改参数类型,以便两个方法的参数类型相同。要么让它们都接受IAbilityTarget[],要么让它们接受IDamagable[]。我认为前者可能更有意义。

来自您的其他评论:

当然,但在Fireball::Use((函数中,我想缩短寿命,但此属性仅在IDamageable接口中定义!

你可以这样做:

public override void Use( IAbilityTarget[] targets )
{
for( int index = 0 ; index < targets.Length ; ++index ) {
if (targets[index] is IDamagable) {
((IDamagable)targets[index]).Life -= 1 ;
}
}
}

如果你想要一种更安全的方法,

public abstract class Ability<T> where T : IAbilityTarget
{
public abstract bool CanBeUsed( T[] targets );
public abstract void Use( T[] targets );
}    
public class Fireball : Ability<IDamagable>
{
public override bool CanBeUsed( IDamagable[] targets )
{
return true ; // for the sake of the example
}
public override void Use( IDamagable[] targets )
{
for( int index = 0 ; index < targets.Length ; ++index )
targets[index].Life -= 1 ;
}
}

但这会阻止您将Ability<IAbilityTarget>转换为Ability<IDamagable>

有时你只需要接受类型系统的限制。

最新更新