在子类的替代功能中使用子接口

时间:2018-07-19 08:27:31

标签: c# oop inheritance interface

您好,亲爱的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

2 个答案:

答案 0 :(得分:3)

Fireball违反了《里斯科夫换人原则》。

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

但是,如果该引用的基础(运行时)类型确实是Fireball,则传递给方法的那些对象必须实现IDamagable,这是一个更严格的要求。就是您不能在不更改方法调用方式的情况下用Fireball代替Ability

通常,

覆盖必须严格 (需要使用哪些先决条件)(不使用类型,而是使用所传递对象的任何状态)。您受到的限制更多。

答案 1 :(得分:1)

  

由于IDamagable扩展了IAbilityTarget,所以我不明白为什么会收到错误消息。

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

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>

有时候,您只需要接受类型系统的限制即可。