我有两个类,一个是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();
*假设我在线类中有所有正确的方法 或者有更好/不同的方式来做到这一点?
答案 0 :(得分:3)
如果您使用的是.NET 3.5或更高版本,则可以通过以下方式减少这种情况:
List<Entity> entities = new List<Entity>();
// add some lines and some arcs
var lines = entities.OfType<Line>();
然后你只需循环遍历lines
,它将包含所有行(强类型为行),而不包含任何其他内容。
我不是说这是最好的方法;我只是说这是做你正在做的事情的一种方式。我同意Shmoopty这是一个架构问题。
答案 1 :(得分:2)
由于Arc
和Line
共享数据(startx和其他一些字段),我建议您使用公共抽象类作为父类而不是接口。例如,Figure
。
演员阵容还可以,但我宁愿推荐:
Line myLine = Entities[0] as Line;
如果Entities[0]
无法转换为Line
,则返回null,而不是抛出异常。您可以在之后检查myLine
是否为空。
答案 2 :(得分:1)
答案 3 :(得分:1)
让界面实现“大小”属性(或称之为“magnitue”或“Range”......)
这将映射到Arc的半径和线条长度。
然后你可以获得Entity.Size。
答案 4 :(得分:0)
当您将Arcs从列表中取出时,这取决于您希望如何处理Arcs。如果您尝试将Arc转换为Line,则会出现运行时错误,因此对于初学者,您应该检查您正在使用的实体是否为Line。
处理Arcs的一种方法是使用Null对象模式。向Arc添加一个返回0的长度方法可能是有意义的。这样,从列表中检索对象的代码就不必关心它们是什么类型。
答案 5 :(得分: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。
答案 6 :(得分:0)
List<object>
可能会起作用,具体取决于您打算对列表执行的操作。如果您正在使用一定数量的类型
假设我有一个需要访问的不同类的属性列表。它们都存储在格式为 Model.Property 的字符串列表中。 然后我在对象列表中有一个对象列表。
foreach(object model in models)
{
Assembly assembly = Assembly.GetExecutingAssembly();
Type type = assembly.GetType(model.GetType().FullName);
foreach (string match in matches)
{
string property = match.Replace($"{model.GetType().Name}.", "");
if (match == $"{model.GetType().Name}.{property}")
{
PropertyInfo prop = type.GetProperty(property);
string value = prop.GetValue(model).ToString();
}
}
}