Создание общего списка объектов в C#
В качестве вступления я создаю базовый движок Quadtree для личных целей обучения. Я хочу, чтобы этот движок имел возможность работать с множеством различных типов фигур (в данный момент я использую круги и квадраты), которые будут все перемещаться в окне и выполнять какие-то действия при столкновении.
Вот мои объекты формы, как они у меня до сих пор:
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;
}
}
Теперь мой вопрос, как мне создать общий список (List<T> QObjectList = new List<T>();
) в C#, чтобы я мог иметь один список, содержащий все эти различные фигуры, которые могут иметь разные свойства (например, QCircle имеет свойство radius), а QSquare имеет свойство sideLength)? Пример реализации также будет полезен.
Я просто знаю, что есть глупо очевидный ответ на этот вопрос, но я все равно был бы признателен за любую помощь. Я пытаюсь вернуться в C#; очевидно, прошло какое-то время...
8 ответов
Вам нужно использовать даункинг
Хранить объекты в списке с базовым классом
List<QShape> shapes = new List<QShape>
Затем вы можете безопасно перебросить объект, если вы знаете, что это, например,
if(shapes[0] is QSquare)
{
QSquare square = (QSquare)shapes[0]
}
Вы также можете неявно понижать объекты
QSquare square = new Square(5,0,0,"Blue");
QShape shape = square
Для получения дополнительной информации прочитайте разделы Upcasting и Downcasting здесь
Это то, что вы хотите?
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;
}
}
}
Вы должны реализовать Interface
, Например
public interface IHasLength
{
int Length;
}
Тогда в реализации вы можете сделать
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; } }
}
Наконец, в вашем списке:
List<IHasLength> shapesWithSomeLength = new List<IHasLength>();
Теперь ваш список может содержать все, что реализует IHasLength
будь то QCircle
, QShape
или даже QDuck
если вы хотите, пока он реализует IHasLength
,
Вы можете хранить их в List<QShape>
но это будет означать, что вы не можете получить доступ к типо-специфическим свойствам.
Как правило, вы можете подойти к этому, предоставив общий интерфейс в базовом классе и переопределив поведение в подклассах. Таким образом, общий интерфейс может скрывать различные варианты поведения. Например, Grow
Метод может скрыть сложности растущих элементов различной формы и может быть вызван без явного знания формы, на которой он работает.
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;
}
}
Я чувствую, что что-то упустил, но...
List<QCircle> circleObjects = new List<QCircle>();
а также
List<QSquare> squareObjects = new List<QSquare>();
будет работать на отлично
РЕДАКТИРОВАТЬ:
Ах, я не понял, о чем спрашивают.
Да как твой QCircle
а также QSquare
классы наследуются от QShape
Вы можете просто сделать.
List<QShape> shapes= new List<QShape>();
Стоит отметить, что если вы хотите получить доступ к radius
собственность всех QCircle
находится в этом списке, то вам придется фильтровать список по типу.
Вы можете использовать комментарий Яна Мерсера List<QShape>
А вот как бы вы его заполнили:
List<QShape> shapes = new List<QShape>();
QCircle circle = new QCircle();
shapes.Add(circle);
Чтобы распаковать его:
QCircle circle = (QCircle) shapes[0];
Если вам нужно вызвать метод из базового класса, не нужно распаковывать, просто используйте его.
public Class abstract Base<T>
{
public abstract List<T>GetList();
}
тогда сделай это
public class className:Base<ObjectName>
{
public override List<T>GetList()
{
//do work here
}
}
хранения
Вы уже на правильном пути со своими определениями классов. Что вам нужно сделать, это сделать List
суперкласса (в этом случае QShape
), который сможет вместить все ваши фигуры.
Вот пример того, как вы бы это сделали:
List<QShape> objects = new List<QShape>();
objects.add(new QCircle(...));
objects.add(new QSquare(...));
Доступ к
Проблема здесь состоит в том, чтобы дифференцировать то, что когда-то есть в списке. Это сделано с getType()
а также typeof()
методы C#. (У Джона Скита отличный ответ о том, как это сделать). В основном это выглядит так:
if(objects.get(some_position).getType() == typeof(QCircle))
QCircle circle = objects.get(some_position);
else if(/* like above with QSquare */)
QSquare square = objects.get(some_position);
После того, как вы это сделаете, вы можете возобновить использование ваших объектов, как обычно. Но если вы попытаетесь получить доступ к ним из списка, вы можете использовать только те методы и переменные, которые QShape
имеет, поскольку каждый объект, помещенный в список, будет приведен к нему.