C# 在运行时更改方法行为的最简单方法是什么?

C# 在运行时更改方法行为的最简单方法是什么?,c#,C#,我有一个名为“敌人”的类,我想有多种类型的敌人。每个敌人都会有不同的移动行为,例如,一个人可能会直接朝玩家移动,一个人可能会保持一定的距离,等等。我想知道的是,在一个类中,有多个不同行为的最简单方法是什么 我目前的想法是使用开关,并将所有行为包含在一个方法中 public class Enemy { public int health; public int damage; // etc etc public void Move(string type)

我有一个名为“敌人”的类,我想有多种类型的敌人。每个
敌人
都会有不同的移动行为,例如,一个人可能会直接朝玩家移动,一个人可能会保持一定的距离,等等。我想知道的是,在一个类中,有多个不同行为的最简单方法是什么

我目前的想法是使用
开关
,并将所有行为包含在一个方法中

public class Enemy
{
     public int health;
     public int damage;
     // etc etc

     public void Move(string type)
     {
        switch(type)
        {
             case "charge":
                  chargeMove();
                  break;
             case "maintain":
                  maintainMove();
                  break;
        }
     }

     private void chargeMove()
     {
          //stuff
     }

     private void maintainMove()
     {
          //stuff
     }

     //all the behaviors
}
我想知道的是,最好将所有内容都保留在这样一个函数中,还是为继承自
敌方
的每种敌方类型创建一个新类?我更愿意将其保留为一个类,这样所有的敌人都可以很好地存储到
IList
,而无需我进行任何强制转换来使用任何特定的函数

如有任何建议,将不胜感激

编辑:感谢所有的回复,我想我将使用Alastair提出的接口方法。

使用代理:

public delegate void DoSth();
在运行时,将此委托与不同的实例相关联

请参见此处。

听起来这将很有用

我认为你的建议是为每种敌人类型设置一个新类是一个好的建议。您仍然可以在一个
列表中存储不同敌人类型的所有不同对象,因为它们都从该类继承

你也可以考虑将<代码>敌人类抽象,但这不是必要的。


如果您确实需要知道列表中特定对象的特定类型,您可以使用或

我将创建多个从同一接口派生的类。您仍然可以拥有接口类型的列表。这是一个很好的例子。如下所示:

interface iEnemy
{
    public void Move();
}

class Troll : iEmeny
{
    public void Move()
    {
       Console.WriteLine("troll moves!");
    }
}

class Ogre : iEmeny
{
    public void Move()
    {
       Console.WriteLine("ogre moves!");
    }
}
然后在代码中可以执行以下操作:

List<iEnemy> enemies = new List<iEnemy>();
enemies.Add(new Troll());
enemies.Add(new Ogre());

foreach(iEnemy e in enemies)
{
    e.Move();
}
//Output would be:
//troll move!
//ogre move!
List敌人=新列表();
添加(新巨魔());
添加(新食人魔());
foreach(敌人中的iEnemy e)
{
e、 Move();
}
//产出将是:
//巨魔行动!
//食人魔行动!

这还将允许您遵循以下规则,即实体应为扩展而打开,但为修改而关闭。

我将使用派生类。这正是它们的目的——在调用相同的方法时提供不同的行为

如果不想强制转换,则在基类上定义所有方法。如果派生类不重写某个特定的方法,它将获得基类中定义的任何行为,但如果您从未调用它,这并不重要。如果你想,如果你不想使用typeof之类的,你仍然可以有一个整数“type”字段

public enum EnemyType
{
    Zombie,
    Vampire
}

public class Enemy
{
    private EnemyType mType;

    protected Enemy(EnemyType type) { mType = type; }

    public EnemyType getEnemyType() { return mType; }

    public void walk() { }

    public void attack() { }

    public void eatBrains() { }
}

public class Vampire : Enemy
{
    public Vampire : base(EnemyType.Vampire) { }

    public void walk()
    {
        // walk like a vampire
    }

    public void attack()
    {
        // attack like a vampire
    }

    // don't bother implementing eatBrains() because vampires don't do this
    // calling code will use getEnemyType() and won't bother call our eatBrains() method anyway
}

虽然这可能更适合于站点,但您可能需要考虑使用某种构图。

定义描述移动描述的接口。例如

public interface IMovementBehavior
{
    void Move(Enemy enemy);
}
然后你会有很多不同的行为来描述不同的动作。 例如

所以你的敌方建筑商可能会有这样的情况:

public Enemy(IMovementBehavior movementBehavior) { ... }
这意味着你可以很容易地插入和输出不同的移动行为,而不必用所有不同的移动行为填充你的敌人职业


这被称为

最好的方法是为每个敌人创建类,这是OOP中的多态性概念,如果只有特定类型的敌人将
EatBrains
。。他引用不想强制转换作为不使用类的理由。因为它只供内部使用,所以我指出,他可以获得类的好处,而不必付出“正确”的代价。此外,函数总是添加到基类中,而不是在所有子类中实现。toString()在大多数OO语言中都是一个很好的例子。
toString
是一个不好的例子,因为它有一个所有对象都依赖的默认实现(并且是框架的基本部分)。这是我的观点。。敌人的衍生产品,即。。说
Policer
不需要
EatBrains
方法。不需要它,但它没有理由不能简单地抛出,例如,您经常看到的修改只读集合类的方法。它们很少实现单独的IReadOnlyCollection接口。或者在不可搜索的流上搜索的方法。谢谢,这似乎是一个简单、灵活的解决方案!
public Enemy(IMovementBehavior movementBehavior) { ... }