【问题标题】:C#: Does function return type override object casting?C#:函数返回类型是否覆盖对象转换?
【发布时间】:2017-09-25 15:59:26
【问题描述】:

我不确定用什么技术术语来描述这种情况,所以这是我的情况。

上下文:

我正在为游戏设计一个物品系统。目前,存在一个 BaseGameItem 类,其中包含 Id、名称、图标等基本参数... 到目前为止,还有另一个派生自它的类,称为 PickupItem,它实现了 IPickupItem。在我尝试通过 Item Spawner 初始化这些对象之前,一切似乎都很好。

关键思想是,存在一个持久性数据库, spawner 是指在初始化对象时,它会继续 吐出完全初始化的对象,如图:

        /// <summary>
    /// Initialize Game Item:
    /// Given an Item ID, constructs a BaseGameItem object and returns it to instigator.
    /// Refers to ItemInfoTable to initialize object parameters.
    /// </summary>
    /// Paramater - Int ID: The provided Item ID
    /// Parameter - out BaseGameItem Item: The constructed object returned to the instigator.
    public static void InitializeGameItem(int id, out BaseGameItem item)
    {
        FItemInfoData itemInfoData = ItemTable.Find(x => x.ItemID == id);

        if(itemInfoData.ItemID != id)
        {
            itemInfoData = ItemTable[0];
        }

        // Check if item is a Pickup. If so, then item is returned as a PickupPlaceable.
        // Otherwise, initialize to BaseGameItem.
        item = itemInfoData.IsPickupItem ? new PickupPlaceable() : new BaseGameItem();

        item.SetItemName(itemInfoData.ItemName);
        item.SetItemDescription(itemInfoData.ItemDescription);
        item.SetItemIcon(itemInfoData.ItemIcon);
        item.SetItemMesh(itemInfoData.ItemMesh);
    }

我的问题如下:

  1. 为了知道我应该返回哪个类或该项目正在实现什么接口,我在数据库中添加了一个布尔值来检查它是否是一个 Pickup 对象。这是一个明智的决定吗?如果您能指导我阅读此类场景中使用的文章或设计模式,我将不胜感激。
  2. 然而,我主要关心的是返回类型。我正在使用 out 关键字并输出 BaseGameItem 对象。但是,在我的三元条件下,我可以将其初始化为派生的 Pickup 项目。函数输出是否会将其转换为 BaseGameItem,从而失去所有拾取功能?还是它将任何派生类解释为有效的返回类型?
  3. 如果对象被返回为 BaseGameItem 无论如何,接收者是否应该知道他们必须将其强制转换为派生类?如何解决这个问题,我是否只是在初始化函数中返回一个布尔值,指示它是一个 Pickup,以便接收器知道何时适当地投射?

【问题讨论】:

  • 实际运行代码时发生了什么?
  • 我觉得这个问题更适合Code Review
  • 可能值得注意的是,您在施放时不会失去对象的功能;您只会失去对功能的访问权限。

标签: c# inheritance unity3d interface polymorphism


【解决方案1】:
  1. 为了知道我应该返回哪个类或该项目正在实现什么接口,我在数据库中包含了一个布尔值来检查它是否是一个 Pickup 对象。这是一个明智的决定吗?如果您能指导我阅读此类场景中使用的文章或设计模式,我将不胜感激。

您与factory method 走在正确的轨道上:

  1. 然而,我主要关心的是返回类型。我正在使用 out 关键字并输出 BaseGameItem 对象。但是,在我的三元条件下,我可以将其初始化为派生的 Pickup 项目。函数输出是否会将其转换为 BaseGameItem,从而失去所有拾取功能?还是它将任何派生类解释为有效的返回类型?

返回类型不会因向上转换而失去功能:

基类可以定义和实现虚方法,派生类可以覆盖它们,这意味着它们提供自己的定义和实现。在运行时,当客户端代码调用该方法时,CLR 会查找对象的运行时类型,并调用该虚拟方法的覆盖。因此,在您的源代码中,您可以调用基类上的方法,并执行派生类的方法版本。

-- Polymorphism (C# Programming Guide)

  1. 如果对象被返回为 BaseGameItem 无论如何,接收者是否应该知道他们必须将其强制转换为派生类?如何解决这个问题,我是否只是在初始化函数中返回一个布尔值,指示它是一个 Pickup,以便接收者知道何时适当地投射?

不,“因此,在您的源代码中,您可以调用基类上的方法,并导致执行派生类版本的方法。”但是,当您需要派生类的特定功能时,例如PickupPlaceable.Pickup(),您必须先向下转换它

编辑:

问题变成了,接收者如何知道基础项目是否应该被施放。

您可以随时使用isas 关键字检查是否可以提取物品:

if (item is PickupPlaceable)
{
     var loot = (PickupPlaceable)item;
     loot.Pickup();
}

var loot = item as PickupPlaceable;
if (loot != null)
{
     loot.Pickup();
}

【讨论】:

  • 感谢您的精心回复!关于最后两点,我的印象是我的设计没有使用这种方法,对吗?因为 BaseGameItem 类根本没有实现 Pickup 接口。即使父类不使用虚拟方法,是否可以用虚拟方法“污染”父类?请注意,为了练习,我打算派生另一个名为“Interactable”的类,它也不应该具有 Pickup 功能。
  • @AAS.N 当然可以使用抽象方法,但在你的情况下,我认为你应该使用接口,例如你可以有 Pickeable 接口和 pick 方法,然后你的每个项目希望成为可挑选的将实现该接口,Interactable 也是如此。
  • @FilipRistic 是的,这正是我最初设计的方式。问题源于 Pickup 类是 BaseGameItem 类的子类这一事实。只有 Pickup 类实现了 IPickup,因为基类可以是不打算被拾取的对象的父类。因此,每当项目生成器尝试初始化游戏项目时,它都必须输出一个 BaseGameItem 对象,如上面的代码所示。然后接收对象的类必须将其转换为实现所需功能的特定子级。问题就变成了,接收者如何知道基础项目是否应该被施放。
  • @Xiaoy312 对不起,如果我听起来很重复,但生成器返回一个 BaseGameItem 实例。由于 Pickup 是 BaseGameItem 的子类,“is”关键字不会总是返回 true 吗? docs.microsoft.com/en-us/dotnet/csharp/language-reference/… 如果 InitializeGameItem 函数将项目初始化为新的 PickupPlaceable 而不是 BaseGameItem,我只希望它返回 true。
  • BaseGameItem 不一定是Pickup,但Pickup 始终是BaseGameItem。假设Pickup 派生自BaseGameItem
猜你喜欢
  • 1970-01-01
  • 2010-11-06
  • 2018-03-18
  • 2019-08-21
  • 1970-01-01
  • 1970-01-01
  • 2021-06-05
  • 1970-01-01
  • 2018-06-15
相关资源
最近更新 更多