尊敬的OOP专家,
如果之前有人问过这个问题,我很抱歉,我还没有找到任何类似的问题(我可能没有合适的词语来解释(。
为了制作一个灵活的系统,我尝试使用Interfaces
和Abstract
类。所涉及的实体非常简单:
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>
。
有时你只需要接受类型系统的限制。