Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a generic list of objects in C#

By way of an intro, I'm creating a basic Quadtree engine for personal learning purposes. I'm wanting this engine to have the capability of working with many different types of shapes (at the moment I'm going with circles and squares) that will all move around in a window and perform some sort of action when collision occurs.

Here are my shape objects as I have them so far:

public class QShape {
    public int x { get; set; }
    public int y { get; set; }
    public string colour { get; set; }
}

public class QCircle : QShape {
    public int radius;
    public QCircle(int theRadius, int theX, int theY, string theColour) {
        this.radius = theRadius;
        this.x = theX;
        this.y = theY;
        this.colour = theColour;
    }
}

public class QSquare : QShape {
    public int sideLength;
    public QSquare(int theSideLength, int theX, int theY, string theColour) {
        this.sideLength = theSideLength;
        this.x = theX;
        this.y = theY;
        this.colour = theColour;
    }
}

Now my question is, how do I create a generic list (List<T> QObjectList = new List<T>();) in C# so I can have one list containing all these various shapes that may have different properties (e.g., QCircle has the "radius" property while QSquare has the "sideLength" property)? An example of implementation would be helpful as well.

I just know that there is a stupidly obvious answer to this question but I'd appreciate any help anyway. I'm trying to get back into C#; it has obviously been a while...

like image 724
Djentleman Avatar asked Jul 06 '12 01:07

Djentleman


4 Answers

Is this what you want?

public class QShape
{
    protected QShape() { }
    public int x { get; set; }
    public int y { get; set; }
    public string colour { get; set; }
}

public class QCircle : QShape
{
    public int radius;
    public QCircle(int theRadius, int theX, int theY, string theColour)
    {
        this.radius = theRadius;
        this.x = theX;
        this.y = theY;
        this.colour = theColour;
    }
}

public class QSquare : QShape
{
    public int sideLength;
    public QSquare(int theSideLength, int theX, int theY, string theColour)
    {
        this.sideLength = theSideLength;
        this.x = theX;
        this.y = theY;
        this.colour = theColour;
    }
}
class Program
{
    static void Main(string[] args)
    {
        List<QShape> list = new List<QShape>();
        list.Add(new QCircle(100, 50, 50, "Red"));
        list.Add(new QCircle(100, 400, 400, "Red"));
        list.Add(new QSquare(50, 300, 100, "Blue"));


        foreach (var item in list.OfType<QCircle>())
        {
            item.radius += 10;
        }

        foreach (var item in list.OfType<QSquare>())
        {
            item.sideLength += 10;
        }
    }
}
like image 120
John Alexiou Avatar answered Nov 17 '22 05:11

John Alexiou


You need to use downcasting

Store the objects in a list with the base class

 List<QShape> shapes = new List<QShape>

You can then upcast the object safely if you know what it is e.g.

if(shapes[0] is QSquare)
{
     QSquare square = (QSquare)shapes[0]
} 

You can also implicitly downcast objects

QSquare square = new Square(5,0,0,"Blue");
QShape shape =  square

For more information read the Upcasting and Downcasting sections here

like image 29
Joel Avatar answered Nov 17 '22 05:11

Joel


You should implement an Interface. For example

public interface IHasLength
{
    int Length;
}

Then in the implementation you can do

public class QSquare : QShape, IHasLength {
    public int sideLength;
    public QSquare(int theSideLength, int theX, int theY, string theColour) {
        this.sideLength = theSideLength;
        this.x = theX;
        this.y = theY;
        this.colour = theColour;
    }
    public int Length { get { return sideLength; } }
}
public class QCircle : QShape, IHasLength {
    public int radius;
    public QSquare(int theSideLength, int theX, int theY, string theColour) {
        this.sideLength = theSideLength;
        this.x = theX;
        this.y = theY;
        this.colour = theColour;
    }
    public int Length { get { return radius; } }
}

FInally, in your list:

List<IHasLength> shapesWithSomeLength = new List<IHasLength>();

Now your list can hold ANYTHING that implements IHasLength whether it's a QCircle, QShape, or even a QDuck if you want as long as it implements IHasLength.

like image 3
Pete Avatar answered Nov 17 '22 05:11

Pete


You could store them in a List<QShape> but this would mean that you could not access type-specific properties.

Generally, you might approach this by providing a common interface in your base class, and overriding behaviour in subclasses. In this way, a common interface can hide a diverse bunch of behaviours. For instance a Grow method could hide the complexities of growing items of different shape and could be called without explicit knowlege of the shape upon which it is operating.

public abstract class QShape {
    public abstract void Grow(int amt);
}
public class QSquare : QShape {
    private int sideLength;
    public override void Grow(int amt)
    {
        sideLength+=amt;
    }
}
public class QCircle : QShape {
    private int radius;
    public override void Grow(int amt)
    {
        radius+=amt;
    }
}
like image 2
spender Avatar answered Nov 17 '22 04:11

spender