【问题标题】:Generics and Inheritance: Using generics with a base-class and its sub-classes泛型和继承:将泛型与基类及其子类一起使用
【发布时间】:2016-05-22 11:15:40
【问题描述】:

在过去的几天里,我一直在尝试开发一个 UI 项目拖放系统,并使其尽可能灵活(它是使用 Unity 3D 开发的,尽管这与我的问题并不完全相关)。在我准确解释问题所在之前,这是代码:

public class ItemHandler<ItemType> : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IHoverHandler where ItemType : Item {

     public ItemType item { get; protected set; }

     ...
}

public abstract class SlotHandler<ItemType> : MonoBehaviour, IDropHandler where ItemType : Item {

    ...

    public ItemHandler<ItemType> itemHandler { get; protected set; }

    ...

    public void OnDrop(PointerEventData eventData) {

        // Retrieve the slots
        SlotHandler<Item> originalSlotHandler = ItemManager.draggedItemParent.GetComponent<SlotHandler<Item>>();
        SlotHandler<ItemType> destinationSlotHandler = this;

        // Retrieve the handlers from their items
        ItemHandler<Item> originalItemHandler = originalSlotHandler.itemObject != null ? originalSlotHandler.itemObject.GetComponent<ItemHandler<Item>>() : null;
        ItemHandler<ItemType> destinationItemHandler = destinationSlotHandler.itemObject != null ? destinationSlotHandler.itemObject.GetComponent<ItemHandler<ItemType>>() : null;

        // Add the original item to the destination slot
        bool canAddToDestination = originalSlotHandler is SlotHandler<ItemType> || originalSlotHandler is SlotHandler<Item>;
        bool canRemoveFromOrigin = originalSlotHandler is SlotHandler<ItemType> || destinationSlotHandler is SlotHandler<Item>;

        // Swap the items
        if(canAddToDestination == true && canRemoveFromOrigin == true) {

            // Remove the old items
            originalSlotHandler.RemoveItem();
            destinationSlotHandler.RemoveItem();

            // Add the new items
            originalSlotHandler.AddItem(destinationItemHandler);
            destinationSlotHandler.AddItem(originalItemHandler);

            ...
        }

        ...
    }

    public virtual void AddItem(ItemHandler<ItemType> itemHandler) {

        this.itemHandler = itemHandler;
    }
}

这背后的想法是让 ItemHandler 具有给定类型(例如 ItemHandler、ItemHandler 等),然后只允许将它们放入相同类型或相同基本类型的 SlotHandler 中。

  • ItemHandler 进入 SlotHandler 和 SlotHandler
  • ItemHandler 进入 SlotHandler 和 SlotHandler。

我尝试这样做是因为我想限制我可以在哪些插槽中放置特定物品。

问题在于我必须考虑特定类型的插槽,SlotHandler(您在代码 sn-p 中看到的 Item 类,它是所有 Items 的基类)。库存槽应该能够容纳每种类型的物品。

现在,由于 ItemHandler 不是 ItemHandler 的子类(或任何其他派生的 Item 类型),这将不起作用。现在你可能会说为什么不只存储 Item 而不是存储 Generic ItemType?我会,但这里的想法是派生 SlotHandler 并在每个子类中具有特定的行为(将项目放入不同类型的插槽应该做不同的事情)。

我想我明白为什么这不会像我尝试的那样工作,但我找不到我觉得舒服的解决方案,所以我想我会尝试寻求帮助。抱歉,如果我对问题不够清楚,请告诉我是否应该解决问题。

提前感谢您尝试解决我的问题的任何帮助!

【问题讨论】:

  • 你是在 UI 系统(Canvas)还是在主游戏场景中这样做?
  • Unity 是一个 ECS 系统。无论如何,它都不是一个面向对象的系统。 (Unity 目前碰巧用来编写组件的语言 c# 确实是一种 OO 语言。但那没什么,无关紧要。他们可以随时更改为组件的其他语言。)我可能不会这样做像你正在做的任何事情。我要做的是编写一个“可拖动”组件,您可以将其附加(当然是任何东西 - 它是一个 ECS 系统),这使得该东西可拖动。这就是它的结束。故事结局。关于“插槽”,如果您希望它们表现得...
  • ...以某种方式表现 - 然后为此编写一个行为。即,他们可能会拒绝一些拖到他们身上的物品,在某些情况下他们可能会放烟花......无论如何。那种“插槽行为”,当然(它是 Unity),你应该能够放下任何东西。这是最近一篇关于相关问题的文章stackoverflow.com/a/37243035/294884
  • 简而言之,您可能会将 the thing 与“您碰巧编写的可以放在 Unity 中的对象上的组件”混淆。请注意,例如您使用了语法时态SlotHandler。什么是 SlotHandler? Unity 中不存在这样的东西,Unity 中也不可能存在。它只是一个游戏对象。如果你想做一个行为,也许是AcceptDrops,那么就去做吧。就像您在 Unity 中编写的任何脚本(也称为任何组件、任何行为)一样,它只是您可以放置​​在任何 GameObject 上的一种行为。
  • @JoeBlow:您的整个解释如果链接到“ECS 系统”会更有意义。

标签: c# unity3d


【解决方案1】:

在我看来,尽管您需要跟踪类型,但您真正需要的只是记录每个项目的父母类型(及其父母)。与其在编程意义上使用类型本身,不如只使用字符串标签并在基类中为项目的有效类型层次结构创建一个列表。这样您就可以拥有复杂的连接和组织,只需将单个字符串与列表进行比较即可查看它是否有效。

using UnityEngine;
using System.Collections.Generic;

public class Item {

  public List<string> validInvTypes = new List<string>();

  //...
}

public class Gem : Item {

  //...
}

public class Ruby : Gem {

  public Ruby()
  {
    validInvTypes.Add("Ruby");
    validInvTypes.Add("Gem");
    validInvTypes.Add("Item");
  }

  //...
}

public abstract class SlotHandler<ItemType> : MonoBehaviour, IDropHandler where ItemType : Item {

  //...

  public void OnDrop(PointerEventData eventData)
  {
    //...

    //string currentInvSlot
    //Item droppedItem

    if (droppedItem.validInvTypes.Contains(currentInvSlot))
      // Swap the items...
  }
}

【讨论】:

  • 这会起作用并且可能是一个更简单/更简单的解决方案,我现在尝试做的事情是假设这些项目最初位于正确的插槽中并且我只是在验证项目根据初始槽位类型和最终槽位类型掉落(基本不考虑实际物品类型)。如果我确实遇到这种方法的任何问题,我会试试你的。感谢您的意见!
猜你喜欢
  • 1970-01-01
  • 2011-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-20
  • 2018-09-27
  • 2015-09-15
相关资源
最近更新 更多