Как я могу хранить разные объекты в одном списке - PullRequest
4 голосов
/ 04 декабря 2009

У меня есть два класса, класс Arc и класс Line

public class Arc
{
     protected double startx;
     protected double starty;
     protected double endx;
     protected double endy;
     protected double radius;

     public Arc(){}
}
public class Line
{
     protected double startx;
     protected double starty;
     protected double endx;
     protected double endy;
     protected double length;
     public Line(){}
}

Но я хочу сохранить дуги и линии в одном списке, поэтому я попробовал подобный интерфейс

public interface Entity
{
     double StartX();
     double StratY();
     double EndX();
     double EndY();
}

Затем я добавил соответствующие методы для каждого класса и добавил код для использования интерфейса. Теперь я могу добавить оба типа объектов в список, но я хочу получить длину из линейного объекта и не хочу добавлять метод длины в дугу или интерфейс. Является ли моя единственная возможность привести линейный объект обратно к линейному объекту, как этот?

List<Entity> entities = new List<Entity>();
entities.Add(new Line(10,10,5,5));
Line myLine = (Line)Entities[0]
double length = myLine.Length();

* Предполагая, что у меня есть все подходящие методы в классе строки.
Или есть лучший / другой способ сделать это?

Ответы [ 6 ]

3 голосов
/ 04 декабря 2009

Если вы используете .NET 3.5 или выше, вы можете сделать это немного менее уродливо следующим образом:

List<Entity> entities = new List<Entity>();

// add some lines and some arcs

var lines = entities.OfType<Line>();

Затем вы просто перебираете lines, который будет содержать все строки (строго типизированные как линии) и ничего больше.

Я не говорю, что это лучший подход; Я только говорю, что это один из способов сделать то, что ты делаешь. Я согласен с Shmoopty, что это проблема архитектуры.

2 голосов
/ 04 декабря 2009

Поскольку Arc и Line совместно используют данные (startx и некоторые другие поля), я предлагаю вам использовать общий абстрактный класс в качестве родительского класса, а не интерфейса. Например, Figure.

С актерами все в порядке, хотя я бы порекомендовал:

Line myLine = Entities[0] as Line;

Возвращается ноль, если Entities[0] не может быть преобразовано в Line, вместо того, чтобы выдавать исключение. После этого вы сможете проверить, является ли myLine нулевым.

1 голос
/ 04 декабря 2009

Пусть интерфейс реализует свойство "Size" (или назовите его "magnitue", или "Range" ...)

Это соответствует радиусу Арки и длине линий.

Тогда вы можете получить Entity.Size.

1 голос
/ 04 декабря 2009

Да, это единственный способ, учитывая ваши ограничения.

Я бы предложил добавить длину в интерфейс (поскольку у дуги есть длина).

Формула может быть найдена здесь .

Или же вы можете добавить метод к интерфейсу, чтобы он вызывал исключение NotImplementedException.

0 голосов
/ 04 декабря 2009

Или есть лучший / другой способ сделать это?

Если ваши объекты происходят от общего класса, вы можете хранить их в одной коллекции. Чтобы сделать что-то полезное с вашими объектами, не отказываясь от безопасности типов, вам нужно реализовать шаблон visitor :

public interface EntityVisitor
{
    void Visit(Arc arc);
    void Visit(Line line);
}

public abstract class Entity
{
    public abstract void Accept(EntityVisitor visitor);
}

public class Arc : Entity
{
    protected double startx;
    protected double starty;
    protected double endx;
    protected double endy;
    protected double radius;

    public override void Accept(EntityVisitor visitor)
    {
        visitor.Visit(this);
    }
}

public class Line : Entity
{
    protected double startx;
    protected double starty;
    protected double endx;
    protected double endy;
    protected double length;

    public override void Accept(EntityVisitor visitor)
    {
        visitor.Visit(this);
    }
}

Как только это произойдет, вы создаете экземпляр EntityVisitor всякий раз, когда вам нужно сделать что-то полезное с вашим списком:

class EntityTypeCounter : EntityVisitor
{
    public int TotalLines { get; private set; }
    public int TotalArcs { get; private set; }

    #region EntityVisitor Members

    public void Visit(Arc arc) { TotalArcs++; }
    public void Visit(Line line) { TotalLines++; }

    #endregion
}

class Program
{
    static void Main(string[] args)
    {
        Entity[] entities = new Entity[] { new Arc(), new Line(), new Arc(), new Arc(), new Line() };
        EntityTypeCounter counter = entities.Aggregate(
            new EntityTypeCounter(),
            (acc, item) => { item.Accept(acc); return acc; });

        Console.WriteLine("TotalLines: {0}", counter.TotalLines);
        Console.WriteLine("TotalArcs: {0}", counter.TotalArcs);
    }
}

И для чего это стоит, если вы открыты для пробования новых языков, тогда помеченные F # союзы + сопоставление с образцом - удобная альтернатива шаблону для посетителей .

0 голосов
/ 04 декабря 2009

Зависит от того, как вы хотите относиться к дугам, когда получаете их из списка. Если вы попытаетесь привести Дугу к Линии, вы получите ошибку времени выполнения, поэтому для начала вам следует проверить, является ли объект, с которым вы работаете, линией.

Одним из способов обработки дуг является использование шаблона нулевого объекта. Возможно, имеет смысл добавить метод длины в Arc, который возвращает 0. Таким образом, код, который извлекает объекты из списка, не должен заботиться о том, какого они типа.

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