【问题标题】:C#: Calling an abstract class method which is implemented through another abstract classC#:调用通过另一个抽象类实现的抽象类方法
【发布时间】:2015-05-03 09:08:40
【问题描述】:

我正在用 C# 创建一个纸牌游戏。

我有一个抽象类“卡片”:

 public abstract class Card
    {

    }

仅用于提供通用父级(即:公爵是一张卡片,船长也是一张卡片)

以下抽象子类继承自 Card,用于进一步指定 Card 的类型(即:Assasin 是 targetCard,duke 是 Moneycard)每个都包含一个名为“Action”的抽象方法,并带有相关参数对自身而言:MoneyCard 有一个将 Player 和 The ChipStack 作为参数的抽象方法,TargetCard 有一个将 Player 和 TargetPlayer 作为输入的抽象方法,等等。

public abstract class PassiveCard : Card
    {
        public abstract void Action();
    }

    public abstract class DeckInteractionCard : Card
    {
        public abstract void Action(Player player, Deck deck);
    }

    public abstract class MoneyCard : Card
    {
        public abstract void Action(Player player, ChipStack chipStack);
    }

    public abstract class TargetCard : Card
    {
        public abstract void Action(Player player, Player targetPlayer);
    }

之后我有了每种卡片的具体实现;它们中的每一个都继承自上述与自身相关的抽象类之一。

当然,每张卡片也完全实现了它从其父抽象类获得的抽象方法。

class Duke : MoneyCard
    {
        public override void Action(Player player, ChipStack chipStack)
        {
            for (int i = 0; i < 3; i++)
            {
                player.TakeChip(chipStack);
            }
        }

        public override string ToString()
        {
            return "Duke";
        }
    }


    class Captain : TargetCard
    {
        //this card gives passive protection: how do we do this
        public override void Action(Player player, Player targetPlayer)
        {
           if (targetPlayer.PlayerChips.Count >= 2)
           {
                targetPlayer.GiveChip(2, player);
           }
        }

        public override string ToString()
        {
            return "Captain";
        }
    }


    class Contessa : PassiveCard
    {
        //this card gives passive protection: how do we do this:
        //give the receiving player the option to bluff, show that he really has a contessa, or deny the other having an assasin
        public override void Action()
        {
            throw new NotImplementedException();
        }

        public override string ToString()
        {
            return "Contessa";
        }
    }


    class Ambassador : DeckInteractionCard
    {
        //this card gives passive protection: how do we do this
        public override void Action(Player player, Deck deck)
        {
            //open dialog: which 2 cards would you like to keep?
            //(now, this action just doubles the players hand)
            for (int i = 0; i < 2; i++)
            {
                player.PlayerHand.HandContent.Add(deck.DrawCard());
            }
        }
        public override string ToString()
        {
            return "Ambassador";
        }
    }


    class Assassin : TargetCard
    {
        public override void Action(Player player, Player targetPlayer)
        {
            if (player.PlayerChips.Count >= 3 && targetPlayer.FoldedAllCards == false)
            {
                for (int i = 0; i < 3; i++)
                {
                    player.PlayerChips.RemoveAt(player.PlayerChips.Count - 1);
                }
                targetPlayer.FoldCard();
            }
        }

        public override string ToString()
        {
            return "Assassin";
        }
    }

现在我的实际问题:

我有一个 Player 课程。玩家类的每个对象都有一个手(手是卡片的集合。)我想在玩家类中创建一个方法,该方法将通用“卡片”作为输入,然后调用与该特定类型相关的操作方法卡片。像这样的:

public void CardAction(Card card)
        {
            card.Action();
        }

上面的例子不起作用:编译器抱怨抽象类 Card 没有调用 action 的方法(这是有道理的)

那么:我将如何创建一个将通用卡片作为输入的方法,并调用与我们作为输入提供的特定卡片相关的 SPECIFIC Action() 方法?

提前致谢,

罗伯特

【问题讨论】:

  • 有充分理由的问题

标签: c# oop inheritance


【解决方案1】:

我认为您的Action 方法过于笼统,但是,您可以使其工作。

不要对Action 进行多个签名,而是创建一个仅接收一个参数Context 的签名。在此参数中,放置操作的不同实现可能需要的所有内容。 示例如下:

public class Context
{
    public Player Player;
    public Player TargetPlayer;
    public ChipStack ChipStack;
    ...
}

public abstract class Card
{
    public abstract void Action(Context c);
}

...

public class Duke : MoneyCard
{
    public override void Action(Context c)
    {
        for (int i = 0; i < 3; i++)
        {
            c.Player.TakeChip(c.ChipStack);
        }
    }

    public override string ToString()
    {
        return "Duke";
    }
}

public void CardAction(Card card)
{
    var c = new Context { Player = p1, TargetPlayer = p2, ChipStack = chips };
    card.Action(c);
}

【讨论】:

  • 这似乎是解决我的问题的绝佳方法。它解决了我最初的问题,但没有摆脱通用 Card 类。非常感谢!
  • 好吧,基本上你不能(除非你切换到接口)并且不应该摆脱卡的通用基类。基类使您能够调用Action 方法,而不管具体的卡类型如何——OOP 多态性的一个很好的例子。一个接口(方法签名)——不同的行为。
【解决方案2】:

我在这里看到了两种解决方案:您可以检查每种卡类型,然后调用适当的方法:

var pCard = card as PassiveCard;
if(pCard != null)
{
    pCard.Action();
    return;
}
var diCard = card as DeckInteractionCard;
if(diCard != null)
{
    diCard.Action(player, deck);
    return;
}
var mCard = card as MoneyCard;
if(mCard != null)
{
    diCard.Action(player, chipStack);
    return;
}
var tCard = card as TargetCard;
if(tCard != null)
{
    tCard.Action(player, targetPlayer);
    return;
}

或者你可以给Card类一个包含所有可能参数的抽象方法:

public abstract class Card
{
    public abstract void Action(Player player, Player targetPlayer, Deck deck, ChipStack chipStack);
}

然后简单地实现这个方法并忽略不必要的参数。或者,如果您不希望此方法可见(仅显示具有所需参数的方法),您可以将基类设置为接口并进行显式实现。

public abstract class PassiveCard : Card
{
    public abstract void Action();

    public override void Action(Player player, Player targetPlayer, Deck deck, ChipStack chipStack)
    {
        Action();
    }
}

public abstract class DeckInteractionCard : Card
{
    public abstract void Action(Player player, Deck deck);

    public override void Action(Player player, Player targetPlayer, Deck deck, ChipStack chipStack)
    {
        Action(player, deck);
    }
}

等等

【讨论】:

  • 您回答的第二个选项似乎是我正在寻找的。我将如何忽略我不需要的参数?
  • 第二个选项意味着每次 OP 添加具有不同签名的卡时,他们都需要更改所有内容。这是一个非常错误的解决方案。运行时类型检查也是极其糟糕的设计。我很感激你写的代码可能比这更好,并试图帮助 OP 解决设计错误,但我认为这些不是好的建议。
  • @Nathan 我的理解是这 4 种卡类型不会被新类型扩展。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-30
  • 2016-11-08
相关资源
最近更新 更多