2017-09-25 96 views
-2

我不确定用什么技术术语来描述这种情况,所以这里是我的情况。C#:函数返回类型覆盖对象转换吗?

语境:

我设计一个项目系统游戏。目前,存在一个包含基本参数(如Id,名称,图标等)的BaseGameItem类... 迄今为止,还存在从它派生的另一个类,称为PickupItem,它实现了IPickupItem。所有似乎都很好,直到我尝试通过Item 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. 为了知道我应该返回哪个类或项目是实现什么接口ing,我在数据库中包含一个布尔值,检查它是否是Pickup对象。这是一个明智的决定吗?如果您能指导我阅读这种场景中使用的文章或设计模式,我将非常感激。
  2. 但我主要关心的是返回类型。我使用out关键字并输出一个BaseGameItem对象。但是,在我的三元条件下,我可以将其初始化为派生Pickup项目。函数输出是否会将其强制转换为BaseGameItem,并因此会丢失所有拾取功能?或者它是否将任何派生类解释为有效的返回类型?
  3. 如果将对象作为BaseGameItem返回,不管接收者是否应该知道他们必须将其转换为派生类?如何解决这个问题,我是否简单地返回一个布尔值,指示它是初始化函数内的Pickup,以便接收者知道何时适当地进行转换?
+0

当你真正运行的代码发生了什么事? – Servy

+0

我认为这个问题更适合于[代码评论](https://codereview.stackexchange.com/) – Xiaoy312

+0

值得注意的是,当你投射时你不会失去对象上的功能;你只是失去了访问功能。 –

回答

0
  1. 为了知道我应该返回或项目实现什么接口,类,我来检查,如果它是一个皮卡对象的数据库包含一个布尔值。这是一个明智的决定吗?如果您能指导我阅读这种场景中使用的文章或设计模式,我将非常感激。

你在正确的轨道上factory method

  • 我最关心的是但返回类型。我使用out关键字并输出一个BaseGameItem对象。但是,在我的三元条件下,我可以将其初始化为派生Pickup项目。函数输出是否会将其强制转换为BaseGameItem,并因此会丢失所有拾取功能?或者它是否将任何派生类解释为有效的返回类型?

  • 返回类型将不会从上溯造型失去功能:

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

    - Polymorphism (C# Programming Guide)

  • 如果对象而不管返回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(); 
    } 
    
    +0

    感谢您的精心回应!关于最后两点,我觉得我的设计没有采用这种方法,对吗?因为BaseGameItem类根本不实现Pickup接口来开始。即使它们不被父级使用,是否可以用虚拟方法“污染”父类?请注意,为了练习,我打算派生另一个名为“Interactable”的类,它也不应该具有Pickup功能。 –

    +1

    @AAS.N当然可以使用抽象方法,但在你的情况下,我认为你应该使用接口,例如你可以使用Pick方法使用Pickeable接口,然后你想要选择的每个项目都可以实现界面,同样适用于Interactable。 – FilipRistic

    +0

    @FilipRistic是的,这正是我最初设计它的方式。这个问题源于Pickup类是BaseGameItem类的一个子类。只有Pickup类实现了IPickup,因为基类可以是不适合拾取的对象的父类。因此,只要item spawner尝试初始化游戏项目,就必须输出一个BaseGameItem对象,如上面的代码所示。然后接收对象的类必须将其转换为实现所需功能的特定子对象。问题变成了,接收者如何知道是否应该投射基础物品。 –