C#A Noob“陷阱”的多态性

问题描述 投票:0回答:2

好吧,所以我有以下问题。我有以下继承的类层次结构

Character ->     Melee    -> Warrior
                          -> Assassin
                          -> Knight
          -> Spellcasters -> Mage
                          -> Druid
                          -> Necromancer

感谢继承和多态,我能够从基类中提取几乎所有冗余代码(warrior,assassin ....)。角色,近战和施法者都是抽象的。

所以我继续编写我的逻辑,想法是让两队近战和施法者相互战斗,直到其中一支球队获胜。最后一个条件让事情非常糟糕,很快。

所以我创建了一个包含所有6个派生类型字符的List,然后我将它们相应地分配到另外两个列表List和List with loops(我的理由是,如果我想在战斗中添加更多字符我只需要添加它们初始列表,其余将自动处理)

我遇到的问题显然是(我应该期待它)我在派生类(近战,施法者,战士等)中失去了对我所有属性/方法的访问权限。

我试着做的是在Melee和Spellcaster课程中添加一个属性DeadTeam,我将用它来跟踪其中一个队伍是否输掉了战斗,当然我无法访问这个属性......

那么我应该如何重构我的逻辑,仍然具有Polymorphism给我的灵活性,而不是编写大量代码并且能够在没有转换的情况下访问我最派生的类?

class EntryPoint
{
    static void Main()
    {
        Random rng = new Random();
        int turnCounter = 0;
        bool gameOver = false;

        List<Character> characters = new List<Character>()
        {
            new Warrior(),
            new Knight(),
            new Assassin(),
            new Mage(),
            new Necromancer(),
            new Druid()
        };

        List<Character> meleeTeam = new List<Character>();
        List<Character> spellTeam = new List<Character>();

        foreach (var character in characters)
        {
            if (character is Melee)
            {
                meleeTeam.Add(character);
            }
            else if (character is Spellcaster)
            {
                spellTeam.Add(character);
            }
        }



        while (!gameOver)
        {
            //meleeTeam[turnCounter].Attack(spellTeam[rng.Next(0, spellTeam.Count)]);
            //spellTeam[turnCounter].Attack(meleeTeam[rng.Next(0, meleeTeam.Count)]);

            turnCounter++;

            if (turnCounter == 3)
            {
                turnCounter = 0;
            }

            gameOver = CheckForWinner(meleeTeam, spellTeam);
        }

    }

    private static bool CheckForWinner(List<Character> meleeTeam, List<Character> spellTeam)
    {

        bool allMeleeDead = true;
        bool allSpellDead = true;

        foreach (var character in meleeTeam)
        {
            if (character.IsAlive == true)
            {
                allMeleeDead = false;
            }
        }

        foreach (var character in spellTeam)
        {
            if (character.IsAlive == true)
            {
                allSpellDead = false;
            }
        }

        if (allMeleeDead)
        {
            Console.WriteLine("Spellcaster team wins!");

            return true;
        }
        else if (allSpellDead)
        {
            Console.WriteLine("Melee team wins!");

            return true;
        }
        else
        {
            return false;
        }
    }
}

我创建的CheckForWinner方法显然可以完成这项工作,但它是一个我不想保留的解决方法,它甚至不是一个解决方法,因为我将继续需要来自我的派生类的其他东西,我仍然无法访问它。我想到这个问题,当我发现这个问题时,我没有关于这个方法的信息,关于团队是死还是活

另一个显而易见的问题是,我想让我的近战团队和拼写团队列表属于近战和施法者类型,但当然这也是不可能的。

附:我知道我可以将它们输入到派生类型中,但这会使我的代码翻两番,这就是我要避免的。

附: 2

我已经改变了我的meleeteam和spellteams并且我将字符列表中的项目添加到这两个列表中,这似乎可以解决问题,我完全重写了我粘贴在上面的其余代码。除此之外,这仍然不能让我访问我最派生的类,但我现在不需要它,似乎没有办法在时间到来时为每种类型编写代码:)

c# inheritance polymorphism
2个回答
0
投票

您的列表的类型为Character,类型Character对派生类的属性和方法一无所知。

您应该将列表中的对象转换为特定的派生类型:

var testMelee = meleeTeam[0] as Melee; 

if (testMelee != null)
{
    // now testMelee is of type Melee and has property DeadTeam (or other properties declared in Character and Melee types) 
    testMelee.DeadTeam = yourValue; // ok, there is such property
}

如果你想要Warrior/Assasin/...的所有属性,那么你应该转换为这种类型:

var testWarrior = meleeTeam[0] as Warrior; 

if (testWarrior != null)
{
    // now testWarrior is of type Warrior and has property DeadTeam (or other properties declared in Character, Melee and Warrior types)        
}

如果您使用C#7或更高版本,则代码可以更改为:

if (meleeTeam[0] as Warrior testWarrior)
{
    // now testWarrior is of type Warrior and has property DeadTeam (or other properties declared in Character, Melee and Warrior types)        
}

此外,在您的帖子中有:

我尝试做的是在Melee和Spellcaster类中添加属性DeadTeam

没有必要在两个类中声明相同的属性。您可以在Character类中简单地声明此属性,并且所有派生类都可以访问它(如果它不是私有的)。


0
投票

我试着做的是在Melee和Spellcaster课程中添加一个属性DeadTeam,我将用它来跟踪其中一个队伍是否输掉了战斗,当然我无法访问这个属性......

这些课程不应该是担心他们所属的整个团队是否已经死亡(这是与团队相关的代码的责任 - 团队类提示提示)。字符类应该有一个方法来确定该特定字符是否已死(f.ex:public bool isAlive())你应该做你的“整个团队死了吗?”循环中的逻辑循环遍历团队中的角色,如果没有活着的角色,则说“是的,他们已经死了”。

在代码中:

public abstract class Character
{
    ...
    public bool isAlive();
}

...
bool allDead = true;
foreach(Character char in spellteam)
{
    if(Character.isAlive())
    {
        allDead = false;
        break;
    }
}

if(allDead)
{
    //Do whatever you need to do when everyone is dead.
}

通常,您需要通过重构和重新创建强制您拥有当前结构的抽象来避免必须在代码中进行类型测试。

这方面的一个例子就是拥有施法者和近战职业 - 你永远不可能有一个既是施法者又是近战的一个类,而不是因为你在游戏中说过,没有两个类都是,但是因为你以一种不可能的方式构建你的游戏。

也许只是对你想要做的事情(或者只是在基类中使用DoAction(actiontype),Attack()或甚至只是TakeYourTurn())进行操作,让类的实现担心最终的操作是什么(因为它知道哪些动作可用)。

通过这种方式,你可以拥有你想要的任何类型的团队,而不受基于他们行为的限制 - 如果你愿意,你仍然可以使用施法者/近战团队,但不限制当前存在的未来可能的添加。

© www.soinside.com 2019 - 2024. All rights reserved.