2009-12-04 53 views
4

我有两个班的电弧类别和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

这是一个比语法更多的体系结构问题。您正在创建*可能有或没有长度*的对象列表。你没有在你的问题中讨论过的是如何或为什么你知道什么时候某个实体会有一个长度。 – 2009-12-04 17:13:54

回答

0

还是有更好/不同的方式来 做到这一点吗?

如果您的对象从普通类下降,那么您可以将它们存储在同一个集合中。为了做任何事情与你的对象有用没有扔掉类型安全,你需要实现visitor pattern

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#的标签联合+模式匹配are a handy alternative to the visitor pattern

2

由于ArcLine共享数据(startx和一些其他字段),我建议你使用一个通用的抽象类作为父类而不是接口。例如,Figure

演员是好的,但我宁愿建议:

Line myLine = Entities[0] as Line; 

这将返回null如果Entities[0]不能转换为Line,而不是抛出异常。您将能够检查myLine之后是否为空。

+0

我已经看到'as'很多。我不明白为什么人们在这种情况下使用它:是一个NullReferenceException更可取的InvalidCastException? – 2009-12-04 17:08:15

+1

我觉得“as”更具可读性。而“as”适用于空值,因此如果在使用该对象之前检查为空,那么该方法就行了。 – ctford 2009-12-04 17:10:43

+1

@Tim:不,但'as' +'== null'优于'is Line' + cast – dtb 2009-12-04 17:11:01

1

是的,这是唯一的方法,因为你的限制。

我建议在界面中添加长度(因为弧确实有长度)。

该公式可以找到here

或者,您可以将该方法添加到接口,并让它抛出一个NotImplementedException。

+0

确实,弧有一个长度,但是一条线的半径是多少?发布的代码只是我的代码的一个简单示例,旨在使问题更清晰。但感谢您的建议。 – Tester101 2009-12-07 13:24:51

1

有接口实现一个“大小”属性(或称之为“magnitue”,或“范围”。)

这映射到圆弧的半径,并以线长度。

然后你可以得到Entity.Size。

+0

这两个量(圆弧半径)和线长,在数学术语中确实映射到完全不同的东西。为什么不使用真正的弧长? – 2009-12-04 17:54:16

0

这取决于您如何在列表中取出弧线时对待弧线。如果您尝试将弧线投射到线条上,则会出现运行时错误,因此对于初学者,您应该检查您正在使用的实体是否为线条。

处理弧的一种方法是使用空对象模式。将一个长度方法添加到返回0的Arc可能是有意义的。这样,从列表中检索对象的代码不必关心它们是什么类型。

3

如果你在.NET 3.5或以上,你可以使这个少一点丑陋这样:

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

// add some lines and some arcs 

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

然后你只需遍历lines,其中将包含所有行(强类型作为线),没有别的。

我不是说这是最好的方法;我只是说这是你做事情的一种方式。我同意Shmoopty这是一个架构问题。

相关问题