You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用多态时未在所有派生类实现virtual方法是否属于不良编程实践?

问题1:当前实现是否属于不良编程实践?

是的,这种实现确实是不推荐的,违反了接口隔离原则(ISP)——该原则要求类不应该被迫依赖它不需要的方法。你当前的实现存在两个明确的问题:

  • 对不能跳跃的Orc来说,基类暴露的Jump()方法无实际作用,Intellisense的提示确实会造成误导,调用者很可能在不知道Orc不能跳的情况下调用该方法,得到无反馈的异常结果。
  • 如果后续你修改基类Jump()的默认实现(比如不再留空,改为抛异常、打日志),所有未重写该方法的派生类都会被影响,不符合开闭原则的预期。

问题2:支持Enemy统一存数组的替代方案

你提到的类型转换繁琐的问题在C#高版本中已经有非常简洁的解决方式,以下是几种常用的优化方案:

方案1:能力接口+模式匹配(最推荐)

把非通用能力抽成独立接口,只有符合条件的派生类实现对应接口,遍历统一数组时用模式匹配判断是否具备对应能力,代码非常简洁:

// 抽离跳跃能力接口
public interface IJumpable
{
    void Jump();
}

abstract class Enemy
{   
    public abstract void Attack();
    // 不再在基类定义Jump方法
}

class Spider : Enemy, IJumpable
{
    public override void Attack()
    {
        Console.WriteLine("Bite attack!");
    }

    public void Jump()
    {
        Console.WriteLine("Spider jump animation");
    }
}

class Goblin : Enemy, IJumpable
{
    public override void Attack()
    {
        Console.WriteLine("Arrow attack!");
    }

    public void Jump()
    {
        Console.WriteLine("Goblin jump animation");
    }
}

class Orc : Enemy
{
    public override void Attack()
    {
        Console.WriteLine("Axe attack!");
    }
}

调用时的写法(完全不需要手动强转):

Enemy[] enemies = new Enemy[] { new Spider(), new Goblin(), new Orc() };
foreach (var enemy in enemies)
{
    enemy.Attack();
    // 模式匹配判断是否可跳跃,一行代码完成判断+取值
    if (enemy is IJumpable jumpable)
    {
        jumpable.Jump();
    }
}

方案2:基类增加能力标记属性

如果你不想引入接口,也可以在基类增加能力判断属性,避免无效的方法调用:

abstract class Enemy
{   
    public abstract void Attack();
    // 派生类各自声明是否支持跳跃
    public abstract bool CanJump { get; }
    public virtual void Jump()
    {
        if (!CanJump) 
        {
            throw new InvalidOperationException("当前敌方单位不支持跳跃");
            // 或者留空,但建议打调试日志避免调用方疑惑
        }
    }
}

class Orc : Enemy
{
    public override bool CanJump => false;
    public override void Attack()
    {
        Console.WriteLine("Axe attack!");
    }
}
// 其他支持跳跃的派生类把CanJump设为true,重写Jump方法即可

方案3:组合实现能力(适合能力多、迭代频繁的场景)

如果你的敌方单位后续会加很多其他能力(比如攀爬、飞行),用组合的方式会比继承/接口更灵活:

// 跳跃行为抽象
public interface IJumpBehavior
{
    void Jump();
}
public class NormalJump : IJumpBehavior
{
    public void Jump() => Console.WriteLine("执行跳跃动画");
}
public class NoJump : IJumpBehavior
{
    public void Jump() { /* 无操作,或者打日志 */ }
}

abstract class Enemy
{
    protected IJumpBehavior _jumpBehavior;
    public abstract void Attack();
    public void Jump() => _jumpBehavior.Jump();
}

class Orc : Enemy
{
    public Orc()
    {
        // 不能跳跃的单位赋值无操作的跳跃行为
        _jumpBehavior = new NoJump();
    }
    public override void Attack()
    {
        Console.WriteLine("Axe attack!");
    }
}

这种方案下你调用Jump()不需要做任何判断,逻辑由各自的行为组件实现,非常适合游戏这类需求变化快的场景。

内容的提问来源于stack exchange,提问作者boulder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 02:06:03