Reputation: 660
I might have worded the title incorrectly but basically what I am wanting to do is have a list of the same object, that has different generic types within it.
The example being this, I have a Tower Defence game that works using a Tile based map. So I have layers (tile layer, object layer, ai layer). So I have this class:
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;
}
}
Now this is fine, this means I can specify a layer that holds a type of object. In the tile layer I want cells that can hold tiles, in objects I want the layer to hold objects etc etc.
Problem is, when I try to create a list of layers in my map class. I am not quite sure on the syntax for a so called "wildcard" type thing like in java (?).
I tried this, to no success:
private List<Layer<dynamic>> layers;
It freaks out when I do this:
layers = new List<Layer<dynamic>>();
layers.Add(new Layer<Tile>(this, "tiles"));
So I am obviously using the incorrect syntax for this.
What should I do in this case?
UPDATE 1:
Following suggestions from people, I have tried using an interface for the common methods inside layer (get cell and what not). The problem with this design is that the layer requires type T in order to determine what type of object each cell can use.
UPDATE 2:
I can not figure out a way to do this any other way, so I decided to go with this solution:
Removed generics all together (almost) and instead of giving cell type T for occupant, I gave it type of GameObject. Now most things do inherit from game object (towers, tiles, obstacles, particles) however none of the AI related code does, so I need to unfortunately change my path finding code to do so.
I now have the following:
public class Layer{
// The name of the layer
private String name;
// All the cells in this layer
private Cell[] cells;
}
Generics have been removed from the Cell class as well:
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;
}
Now I am using a generic method inside the Cell class like so:
/// <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;
}
So this code here, works as intended:
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>();
Although not the nicest looking code, the map system will only be used for this game so I am not particularly worried about writing re-usable code, mostly functional code. Ignore the fact that I have just stuck an enemy in the tile layer but I will add some sort of cheap check (typeof) to make sure I don't mess that up somewhere.
Upvotes: 2
Views: 1775
Reputation: 371
In .NET Framework library it is implemented like this:
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>
{ }
Upvotes: 0
Reputation: 101680
The problem is that Layer<Tile>
and Layer<Ai>
don't have any parent class in common that you could cast both to. The closest is the very general object
that all reference types inherit from.
A common approach in this situation is, as Lasse suggests, define a common interface that they all inherit from:
interface ILayer
{
void CreateCells();
}
public class Layer<T> : ILayer
{
Then your map can have a List
of ILayer
s:
layers = new List<ILayer>();
layers.Add(new Layer<Tile>(this, "Tiles"));
You'll need to think of a way to design your ILayer
interface in such a way that it is not dependent on the generic parameter T
.
Upvotes: 3