【问题标题】:Inherited class with methods taking Child type as a parameter: wrong method being called方法以子类型为参数的继承类:调用了错误的方法
【发布时间】:2015-11-17 23:18:12
【问题描述】:

我正在制作一个纸牌游戏,我有几个控制纸牌行为的脚本。

public class BasicCardModel : Draggable {
    public int hitPoints;
    public GameObject cardObject;
    public static string cardName = "Basic Card";

    public CardStatus cardStatus = CardStatus.None;

    public void copyAttributes(BasicCardModel bcm) {
        Debug.Log("Calling basic copy attributes");
        hitPoints = bcm.hitPoints;
    }
    ...
}

我有几张类似于下面的特殊卡片:

public class AttackCardModel : BasicCardModel {
    public int attackStrength;
    public AttackType attackType;

    public void copyAttributes(AttackCardModel acm) {
        base.copyAttributes(acm);
        attackType = acm.attackType;
        Debug.Log("Attack strength = " + acm.attackStrength);
        attackStrength = acm.attackStrength;
    }
}

我有一个生成这些卡片的控制器对象:

public class GameController : MonoBehaviour {
    public GameObject basicCard, attackCard, defenseCard, eventCard, masterCard;
    public GameObject topPlayerDeck, topPlayerHand, topPlayerField;
    public GameObject bottomPlayerDeck, bottomPlayerHand, bottomPlayerField;
    public GameObject eventDeck;
    // Use this for initialization
    void Start () {
        // Link controller to game objects
        topPlayerDeck = GameObject.Find("TopPlayerDeck");
        topPlayerHand = GameObject.Find("TopPlayerHand");
        topPlayerField = GameObject.Find("TopPlayerField");
        bottomPlayerDeck = GameObject.Find("BottomPlayerDeck");
        bottomPlayerHand = GameObject.Find("BottomPlayerHand");
        bottomPlayerField = GameObject.Find("BottomPlayerField");
        eventDeck = GameObject.Find("EventDeck");

        CardCollection cards = generateCards();

        foreach (BasicCardModel card in cards.cards) {
            if(card is AttackCardModel) {
                createCard<AttackCardModel>(topPlayerHand, card as AttackCardModel, Player.Top, CardStatus.Hand);
                createCard<AttackCardModel>(bottomPlayerHand, card as AttackCardModel, Player.Bottom, CardStatus.Hand);
            }
            else if(card is DefenseCardModel) {
                createCard<DefenseCardModel>(topPlayerHand, card as DefenseCardModel, Player.Top, CardStatus.Hand);
                createCard<DefenseCardModel>(bottomPlayerHand, card as DefenseCardModel, Player.Bottom, CardStatus.Hand);
            }
            else {
                createCard<BasicCardModel>(topPlayerHand, card as BasicCardModel, Player.Top, CardStatus.Hand);
                createCard<BasicCardModel>(bottomPlayerHand, card as BasicCardModel, Player.Bottom, CardStatus.Hand);
            }
        }

        /*
        for(int i = 0; i < 2; i++) {
            createCard<AttackCardModel>(topPlayerHand, Player.Top, CardStatus.Hand);
            createCard<AttackCardModel>(bottomPlayerHand, Player.Bottom, CardStatus.Hand);
        }
        for (int i = 0; i < 2; i++) {
            createCard<DefenseCardModel>(topPlayerHand, Player.Top, CardStatus.Hand);
            createCard<DefenseCardModel>(bottomPlayerHand, Player.Bottom, CardStatus.Hand);
        }
        */
    }

    // Update is called once per frame
    void Update () {

    }

    // For testing, have a CardCollection passed in later
    public CardCollection generateCards() {
        CardCollection cards = new CardCollection();
        //AttackCardModel testcard = new AttackCardModel(4, AttackType.SQLInjection, 3);
        cards.cards.Add(new AttackCardModel(4, AttackType.SQLInjection, 3));
        cards.cards.Add(new DefenseCardModel(5, AttackType.SQLInjection, 1));
        //Debug.Log(testcard.attackStrength + "is attack strength");
        return cards;
    }

    public void createCard<T>(GameObject whereToPut, T objectToCopy, Player player, CardStatus cardStatus) where T : BasicCardModel {
        GameObject new_card = Instantiate(basicCard);
        new_card.transform.SetParent(whereToPut.transform, false);
        Destroy(new_card.GetComponent<BasicCardModel>());
        new_card.AddComponent<T>();
        --->new_card.GetComponent<T>().copyAttributes(objectToCopy); <---
        new_card.GetComponent<T>().linkModelToCardObject(new_card);
        new_card.GetComponent<T>().setUpCard<T>(player, cardStatus);
    }
}

我在使用这一行时遇到了问题(靠近控制器对象的末尾): new_card.GetComponent&lt;T&gt;().copyAttributes(objectToCopy);

它不是为copyAttributes调用子方法,而是调用父方法,因此不会复制我想要的属性。

在我之前的问题中,有人建议使用动态类型作为解决方案。但是,即使GetComponent&lt;ParentClass&gt; 应该获取子类型,它也由于某种原因不起作用,所以我需要使用泛型。

如何强制它调用子方法而不是父方法?

【问题讨论】:

    标签: c# inheritance unity3d polymorphism


    【解决方案1】:

    将您的 BasicCardModelcopyAttributes 方法设为虚拟。

    public class BasicCardModel : Draggable {
        // ...    
        public virtual void copyAttributes(BasicCardModel bcm) {
            // ...    
        }
        // ...    
    }
    

    然后覆盖它AttackCardModel,并将卡片模型转换为派生类型,然后再复制其他属性:

    public override void copyAttributes(BasicCardModel bcm) {
        base.copyAttributes(acm);
        var acm = bcm as AttackCardModel;
        if (acm != null) {
            attackType = acm.attackType;
            Debug.Log("Attack strength = " + acm.attackStrength);
            attackStrength = acm.attackStrength;
        }
    }
    

    【讨论】:

    • 啊!!这很聪明。使用var 的任何理由还是只是方便?
    • @ChrisChambers:请注意,这正是我在回复your other question 时提供的选项之一。我想说这个简化的例子毕竟足以回答你的问题。
    • @PeterDuniho 呵呵,我想我看的不够仔细。我没有看到它的原因是我自己的错,我需要在我正在使用的环境中看到它。
    • 使用var 声明局部变量是可选的。在这种情况下,很清楚它必须是什么类型,因为语句说明了强制转换中的类型。当您分配方法调用的结果时,乍一看可能不太明显。尽管如此,许多风格指南建议始终使用var。如果您不确定,您的 IDE 应该会帮助您检查类型。
    【解决方案2】:

    您需要在类型中反映层次结构 - 一种方法是创建一个接口并在您的 Create 方法中为其添加约束:

    public interface ICopyableFrom<T>
    {
         void CopyAttributes(T src);
    }
    
    public void createCard<T>(GameObject whereToPut, T objectToCopy, Player player, CardStatus cardStatus) where T : BasicCardModel, ICopyableFrom<T>
    {
            GameObject new_card = Instantiate(basicCard);
            new_card.transform.SetParent(whereToPut.transform, false);
            Destroy(new_card.GetComponent<BasicCardModel>());
            new_card.AddComponent<T>();
            new_card.GetComponent<T>().CopyAttributes(objectToCopy);
            new_card.GetComponent<T>().linkModelToCardObject(new_card);
            new_card.GetComponent<T>().setUpCard<T>(player, cardStatus);
    }
    

    然后你需要在你的类中实现它:

    public class AttackCardModel : BasicCardModel, ICopyableFrom<AttackCardModel>
    {
        public void CopyAttributes(AttackCardModel src)
        {
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2016-02-19
      • 2023-03-29
      • 1970-01-01
      • 1970-01-01
      • 2014-05-10
      • 2021-06-17
      • 1970-01-01
      • 1970-01-01
      • 2012-05-11
      相关资源
      最近更新 更多