您好,亲爱的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
。
答案 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>
。
有时候,您只需要接受类型系统的限制即可。