2014-12-21 64 views
2

我可能錯誤地描述了標題,但基本上我想要做的是具有同一對象的列表,它具有不同的通用類型。C#多種類型的通用列表

這個例子,我有一個塔防遊戲,它使用基於Tile的地圖。所以我有層(瓦片層,物體層,ai層)。所以我有這個類:

public class Layer<T>{ 

    // The name of the layer 
    private String name; 

    // All the cells in this layer 
    private Cell<T>[] cells; 

    public Layer(Map map, String name){ 
     cells = new Cell<T>[map.Width * map.Height]; 
     this.name = name; 
     CreateCells(); 
    } 

    /// <summary> 
    /// Fills the array of cells with blank, unoccupied cells 
    /// </summary> 
    private void CreateCells(){ 
     var index = 0; 
     for (var x = 0; x < cells.Length; x++){ 
      for (var y = 0; y < cells.Length; y++){ 
       cells[index] = new Cell<T>(x, y); 
       index++; 
      } 
     } 
    } 

    // Gets the array of cells in this layer 
    public Cell<T>[] GetCells(){ 
     return cells; 
    } 

    /// <summary> 
    /// Gets a cell at a specific index/coordinate 
    /// </summary> 
    /// <param name="x"></param> 
    /// <param name="y"></param> 
    /// <returns>The cell at the X and Y coordinate/index</returns> 
    public Cell<T> GetCellAt(int x, int y){ 
     foreach (var c in cells){ 
      if (c.GetX() != x || c.GetY() != y) continue; 
      return c; 
     } 
     throw new Exception("Cell not found at " + x+"x"+y); 
    } 

    /// <summary> 
    /// Gets the name of this layer 
    /// </summary> 
    /// <returns></returns> 
    public String GetName(){ 
     return name; 
    } 

} 

現在,這是好的,這意味着我可以指定持有Object類型的層。在圖塊圖層中,我希望可以容納圖塊的單元格,對象中我希望圖層可以容納對象等的地方等。

問題是,當我嘗試在地圖類中創建圖層列表時。我對Java(?)中所謂的「通配符」類型的語法不太確定。

我試過了,沒有成功:

 private List<Layer<dynamic>> layers; 

它怪胎的時候我這樣做:

  layers = new List<Layer<dynamic>>(); 
     layers.Add(new Layer<Tile>(this, "tiles")); 

所以我顯然是在利用這種不正確的語法。

我應該在這種情況下怎麼辦?

更新1:

在從人的建議,我已經使用內層的常用方法的接口(送電池和什麼不可以)嘗試。這種設計的問題在於,圖層需要類型T才能確定每個單元格可以使用的對象類型。

更新2:

我不能想出一個辦法做到這一點任何其他方式,所以我決定去與此解決方案:

刪除仿製藥一起和(幾乎)而不是給細胞類型T爲乘員,我給它的類型的GameObject。現在大多數事情都是從遊戲對象(塔,瓦片,障礙物,粒子)繼承而來,然而AI相關的代碼都沒有,所以我不得不改變我的路徑查找代碼來做到這一點。

我現在有以下幾點:

public class Layer{ 

    // The name of the layer 
    private String name; 

    // All the cells in this layer 
    private Cell[] cells; 

} 

泛型已經從Cell類以及去除:

public class Cell{ 

    // The object inside this cell 
    private GameObject occupant; // Note the new occupant type 
    // The width and height of the cell in world coordinates 
    public const float WIDTH = 1, HEIGHT = 1; 
    // The coordinate of this cell 
    private int x, y; 
} 

現在我使用Cell類中的泛型方法如下所示:

 /// <summary> 
    /// Gets the occupant in this cell 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    /// <returns></returns> 
    public T GetOccupant<T>() where T : GameObject{ 
     return occupant as T; 
    } 

所以這裏的代碼,按預期工作:

  layers = new List<Layer>(); 
     layers.Add(new Layer(this, "tiles")); 
     GetLayer("tiles").GetCellAt(5, 5).SetOccupant(new EnemyScientist(null, 5, 1, 6)); 
     Enemy e = GetLayer("tiles").GetCellAt(5, 5).GetOccupant<Enemy>(); 

儘管不是最好看的代碼,地圖系統只會用於這個遊戲,所以我並不特別擔心編寫可重複使用的代碼,主要是功能代碼。忽略我剛剛將一個敵人卡在瓦片層的事實,但我會添加某種便宜的檢查(typeof)以確保我不會把它搞亂。

+3

'新的List <層>'也許?另外C#也有通用約束 - 在文檔中查找「where」。 –

+3

您是否考慮過使用通用位來實現接口?該列表可以是'List >'將不起作用,除非您將它限制爲'out'泛型類型並且僅限於'class'類型。 'out'也只適用於接口和代理,這意味着你不能僅僅通過類型本身來做到這一點。 –

+0

我可以去掉泛型類型的層,而是有三層繼承和接口是,這實際上可能是考慮到每一層都會有相同的字段 – Gibbo

回答

3

的問題是,Layer<Tile>Layer<Ai>沒有共同之處,你都可以轉換爲任何父類。最接近的是所有引用類型繼承的非常一般的object

在這種情況下,常用的方法是,作爲拉塞建議,定義一個通用的接口,它們都繼承:

interface ILayer 
{ 
    void CreateCells(); 
} 

public class Layer<T> : ILayer 
{ 

那麼你的地圖可以有ILayerList S:

layers = new List<ILayer>(); 
layers.Add(new Layer<Tile>(this, "Tiles")); 

你需要想辦法來設計你ILayer接口以這樣一種方式,它不依賴於泛型參數T

+0

謝謝,這看起來越來越像是問題的最可能解決方案 – Gibbo

+0

只需閱讀您的編輯和問題,該圖層需要類型T來確定每個單元格可以容納的類型。所以接口可能不實際工作在這裏 – Gibbo

+0

@Gibbo圖層類仍然可以有一個通用型'T'(看到我用'公共類層行:層{')。只是你的地圖類需要與'T' w.r.t無關。它如何存儲和與圖層進行交互。 (例如,您可能需要在'細胞''從繼承Cell'讓你的'GetCells()'可以返回'名單''而不是名單>')。 – JLRishe

0

在.net庫中它的實現是這樣的:

class Program 
{ 
    static void Main(string[] args) 
    { 
     var layers = new List<ILayer<object>>(); 
     layers.Add(new Layer<string>()); // It is covariance so we can only use reference types. 
    } 
} 

public interface ILayer<out T> 
{ 
    void CreateCells(); 
    ICell<T>[] GetCells(); 
} 

public class Layer<T> : ILayer<T> 
{ } 

public interface ICell<out T> 
{ } 

public class Cell<T> : ICell<T> 
{ } 
+0

這會編譯,但我不確定它最終會提供什麼好處,並且它可能會導致運行時異常不會發生。像這樣:'var layers = new List >; layers.Add(new Layer ()); ICell [] arr = layers [0] .GetCells(); arr [0] =新單元格(); // runtine exception''out'不應該被用來讓編譯器忽略類型安全。 – JLRishe

+0

是的,對象的框在分配後不能更改爲其他類型。意味着這是好的:ICell cell = new Cell (); –