Создание общего списка объектов в 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 ответов


вам нужно использовать downcasting

хранить объекты в списке с базовым классом

 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 здесь


вы должны реализовать 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;
    }
}

это то, что вы хотите?

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;
        }
    }
}

Я чувствую, что мне чего-то не хватает, но...

List<QCircle> circleObjects = new List<QCircle>();

и

List<QSquare> squareObjects = new List<QSquare>();

будет прекрасно работать.

EDIT:

Ах, я не понял, о чем спрашивали.

да, как ваш 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];

Если вам нужно вызвать метод из базового класса, нет необходимости распаковывать, просто используйте его.


хранение

вы уже на правильном пути с определения классов. Что вам нужно сделать, это сделать 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 имеет, так как каждый объект, помещенный в список, будет приведен к нему.


public Class abstract  Base<T>
{
    public abstract List<T>GetList();
}

после этого

public class className:Base<ObjectName>
{
    public override List<T>GetList()
    {
        //do work here 
    }
}