2012-07-15 49 views
5

我想創建一個簡單的「物品清單」,就像在任何RPG中一樣。我有非常基本的類,它有屬性。C#列表<baseClass> - 任何方式來存儲繼承類,也?

無論如何,我有一個基類item並繼承自weaponitem有屬性(名稱,價值,重量,「重要事項」)同樣用於內weapon,但weapon有額外的屬性(攻擊,防禦,速度,利手)。

我有下面的代碼(對不起,如果可讀性是可怕的):

static void Main(string[] args) 
{ 
    List<item> inventory = new List<item>(); 
    inventory.Add(new weapon("Souleater", 4000, 25.50f, false, 75, 30, 1.25f, 2)); 
          //item---------------------------> weapon---------> 

    Console.Write("Name: {0}\nValue: {1}\nWeight: {2}\nDiscardable: {3}\nAttack: {4}\nDefense: {5}\nSpeed: {6}\nHandedness: {7}", 
      inventory[0].Name, inventory[0].BValue, inventory[0].Weight, inventory[0].Discard, 
      inventory[0].Atk, inventory[0].Def, inventory[0].Speed, inventory[0].Hands); 

    Console.ReadLine(); 
} 

基本上,我想要做的就是添加一個新的weapon去庫存,但庫存是List<item>類型。我出於一時的希望,因爲它的繼承,它會被接受。它是,但weapon特定的屬性無法訪問:

('shopSystem.item'不包含'Atk'的定義,也沒有擴展方法'Atk'接受類型的第一個參數「shopSystem.item」可以找到)

那麼,有沒有什麼辦法可以實現我正打算在這裏?有一個「庫存」,它可以存儲item對象,以及weaponarmouraccessory等對象,從item繼承?還值得一提的是,如果我聲明以下內容,我可以訪問所有期望的屬性:

weapon Foo = new weapon("Sword", 200, 20.00f, false, 30, 20, 1.10f, 1); 

非常感謝您的閱讀。

這裏的itemweapon類,如果有人有興趣:

​​

回答

8

爲了訪問特定的繼承類的屬性,你需要轉換到相應的具體類的類型。因此,您不需要訪問inventory[0],而需要訪問(weapon)inventory[0]。另外,如果你正在執行基於底層類的基於不同實現的常見任務,比如將值記錄到控制檯(我認爲這是爲測試完成的,但無論如何都是一個很好的例子),你應該在基類中實現可重寫的方法。

例如,在基類:

public virtual void LogProperties() 
{ 
Console.Write("Name: {0}\nValue: {1}\nWeight: {2}\nDiscardable: {3}\nAttack", 
      this.Name, this.BValue, this.Weight, this.Discard); 
} 

而且在武器類:

public override void LogProperties() 
{ 
Console.Write("Name: {0}\nValue: {1}\nWeight: {2}\nDiscardable: {3}\nAttack: {4}\nDefense: {5}\nSpeed: {6}\nHandedness: {7}", 
      this.Name, this.BValue, this.Weight, this.Discard, 
      this.Atk, this.Def, this.Speed, this.Hands); 
} 

然後,訪問此:

inventory[0].LogProperties(); 
+1

如果我使用'(武器)庫存[0] .Atk',我仍然會得到相同的錯誤信息。 – Deadrust 2012-07-15 22:58:44

+1

嘗試使用((武器)庫存[0])。而不是 – 2012-07-15 22:59:18

+0

是啊,那個工程!謝謝! – Deadrust 2012-07-15 23:00:30

0

你可以實現一個共同的接口(例如IItem)通過它查詢項目的屬性,或者實現一個公共基類,pro可以說是抽象的一個,並將其用作List的類型T。 如果使用後一個選項,請確保在適當的情況下重寫基類的功能,多態性將完成其餘部分。

0

您需要將該值轉換回weapon才能訪問這些屬性,即((weapon)inventory[0]).Atk)。注意圓括號:鑄造比字段存取具有更低的優先權,所以你不能寫(weapon)inventory[0].Atk;這將被解釋爲(weapon)(inventory[0].Atk)

您還需要區分不同的類(例如,您可能也有一個armor類)。最簡單的做法是在基類item上添加一個額外的值,它告訴你對象的實際類型,然後相應地進行轉換。

但是,更好的方法是使用繼承 - 讓item定義一系列可以處理所有通用內容的方法 - 例如,顯示關於該項目的信息 - 然後在繼承類中覆蓋它。也就是說,不是直接訪問所有的數據,而是以一種明智的方式將它結合起來。爲了您的具體的例子,你項目轉換爲字符串,所以它很可能是有意義的重寫的ToString() - 這意味着你將能夠做到這一點:

Console.Write(inventory[0]); 

,並根據是否是一個物品或武器,相應類的ToString()會被調用(通常,你必須調用方法本身,但ToString()是特殊的,因爲所有對象都有該方法,所以Console.Write可以用於您)。

在你的武器類,你會再實現ToString方法是這樣的:

public string ToString() { 
    //Put all of the data in here, like in your example 
    return String.Format("Name: {0}\n...", name, ...); 
} 
+0

我喜歡那個ToString方法。但是,如果我只想訪問其中一個屬性(比如.Atk屬性)以用於數學公式,該怎麼辦?最好是保持它的方式,即'((武器)庫存[0])。我製作的字符串只是爲了測試值是否正確存儲。 – Deadrust 2012-07-15 23:04:12

+0

@Deadrust:是的,你可以做到這一點,你也可以使用'weapon used武器=(武器)庫存[0];'然後訪問usedWeapon上的所有屬性,而不需要額外的鑄造。你甚至可能想早點這樣做,例如將當前裝備的武器存放在「玩家」或「單位」級別,但角色扮演遊戲的方法會有所不同。 – 2012-07-15 23:12:19

+0

另外請記住,你沒有*有*使用ToString這個例子。定義自己的方法和調用它可能同樣有意義 - 這完全取決於您打算如何處理類。 – 2012-07-15 23:16:58

2

您通過使用List<item>物品存放的物品和子類做正確的事!

當您從列表中提取項目時,您只需要做一些工作來測試它是否是weapon或某種其他類型的item

要查看是否在陣列上的位置i元件是weapon,使用as操作者,這將返回null如果該項目不是指定類型(也許是裝甲或一些其他類型的項目)。這被稱爲dynamic演員。即使該項目是null,它也可以工作。

weapon w = inventory[i] as weapon; // dynamic cast using 'as' operator 
if (w != null) 
{ 
    // work with the weapon 
} 

另一種方式做,這是使用is運營商做一個static鑄造前檢查的類型。

if (inventory[i] is weapon) 
{ 
    // static cast (won't fail 'cause we know type matches) 
    weapon w = (weapon)inventory[i];   

    // work with the weapon 
} 
+1

更好的替代第二個測試:'如果(庫存[我]是武器)'。但是你的第一個例子(用'as')可能更可取,因爲它避免了兩次投射對象。 :) – 2012-07-16 00:56:08

+0

@DanJ是的,謝謝!這也避免了空檢查的需要。回答相應調整。 :) – 2012-07-16 01:18:32

+0

當我開始動態地將項目添加到庫存實例時,第一個示例正是我所需要的。非常感謝! – Deadrust 2012-07-16 06:06:57

1

這是Linq在處理多態性時可以成爲你的朋友的地方。鑄造物品的問題是當給定的索引不是正確的類型時。 (即inventory[0]不是weapon(weapon)inventory[0]將拋出。)

您可以通過使用避免了異常:

var myWeapon = inventory[0] as weapon; 

然而,那麼你將處處做#NULL檢查。使用LINQ你得到了很多的靈活性:

var weapons = inventory.OfType<weapon>(); 

從這裏你可以使用WhereFirstOrDefault,等找到你感興趣或者遍歷武器數據。

+0

+1這將是我的方法,它限制了多少代碼必須知道通用庫存。例如,您可以編寫一個方法來處理'List '並將'inventory.OfType ()'傳遞給它。該方法從不需要擔心庫存中存在其他類型的物品。 – 2012-07-16 22:00:24