Creating a generic list of objects in C#
Solution 1
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
Solution 2
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
.
Solution 3
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;
}
}
}
Solution 4
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;
}
}
Solution 5
I feel like i'm missing something but...
List<QCircle> circleObjects = new List<QCircle>();
and
List<QSquare> squareObjects = new List<QSquare>();
will work perfectly well.
EDIT:
Ah, I didn't understand what was being asked.
Yes, as your QCircle
and QSquare
classes inherit from QShape
, you can just do.
List<QShape> shapes= new List<QShape>();
It's worth noting that if you want to access the radius
property of all the QCircle
's in that list, then you are going to have to filter the list based on type.
Related videos on Youtube
Djentleman
Updated on October 25, 2020Comments
-
Djentleman about 3 years
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...
-
Ian Mercer over 11 yearsList<QShape> ??
-
-
Inisheer over 11 yearsThe OP wants one List<T> which will include both. Therefore, it would be List<QShape>.
-
Pete over 11 yearsYeah, that will work but he wants ONE list to hold any type of
QShape
,QCircle
, andQSquare
. -
Djentleman over 11 yearsI should have been clearer, but I'm wanting a single list if possible.
-
spender over 11 yearsIf you're having to test types and cast when dealing with items in generic collections, it's a sign that something has gone wrong.
-
Chris Gessler over 11 yearsQShape should also be abstract. Also, I don't see the use of an interface in your example.
-
spender over 11 yearsI use the term interface in a loose fashion. All objects have an interface by virtue of offering methods and properties... not just those that inherit an
interface
implementation. -
Chris Gessler over 11 yearsWTF is objects.get().getType() ?? And why not just shorten it to if(myObject is MyType)
-
Chris Gessler over 11 yearsI believe you can use the is keyword in your example, it's been available for quite some time. if(myObject is MyType)
-
Pete over 11 years@Djentleman Because your classes don't have common property names. So if you were to create a list of
QShape
and wanted to iterate over it and get the length for all shapes,QCircle
andQSquare
you can't. If you implement the interface then you will be able to do just that. -
Chris Gessler over 11 yearsI learn a lot I didn't know on this site as well... everyday in fact, I think I learn something new.
-
Djentleman over 11 yearsAhh, I see. Very nice. Time to read up on interfaces! Thanks :)
-
Djentleman over 11 yearsI like this answer. Nice and simple. Thanks! Just a quick question on this, however; why would implicitly downcasting objects be useful?
-
Djentleman over 11 yearsI like this, similar to Pete's answer. Thanks!
-
Djentleman over 11 yearsGood answer, very simple and yet informative. Joel's answer is similar but a bit more detailed, but thanks nonetheless!
-
Pete over 11 yearsAgain, because if you are iterating over a list of
QShape
s you can't access properties/methods ofQSquare
orQCircle
. By checking in the if statement to see if it's aQSquare
and casting it if it is you then have access to the properties/methods ofQSquare
. The method in this answer is ok for checking a couple of classes but I'm sure you can see where it quickly gets out of hand if you start adding more and more subclasses. -
Djentleman over 11 yearsSorry, I misunderstood the meaning of downcast. Great answer; very informative. Thank you.
-
Djentleman over 11 yearsJust a quick note for your benefit. I've gone with this sort of approach except I've changed IHasLength to ISpatialNode. This interface is for x,y positioning. Makes more sense, and means I can add any object that requires positioning into my Quadtree.
-
phoog over 11 years@Joel note that
is
tests for assignment compatibility, not type equality, so it's not equivalent. Specifically, for a subclass of QSquare,x.GetType() == typeof(QSquare)
returns false, whilex is QSquare
returns true -
Pete over 11 years@Djentleman Of course you want to use whatever names/properties makes sense for your implementation. I just choose
Length
for the example since that's what you mentioned in your question (radius
,sideLength
). I'm sure it's all clear by now ;) -
matsjoyce about 9 yearsSome explanation would be useful.