This is an elaboration of David Culp's answer. It's quite long, so I'm not posting as a comment.
While the generic interface IList<TPoint>
looks like a good solution, I would recommend to elaborate the semantics a bit further. Also, it's advisory to consider introducing both generic and non-generic versions of some interface (like IEnumerable
plus IEnumerable<T>
).
In your clarified example you should introduce a term describing a finite entity that has a starting and ending point. Generally a curve is not a line but a line is a curve. However, since your curve uses ControlPoint
s to describe its start and end, while the line uses plain Point
s instead, in your specific case a line is not a curve. Instead, both line and curve are something like a shape.
// this interface will denote any shape that has a starting and ending point
public interface IShape
{
IPoint Start { get; set; }
IPoint End { get; set; }
}
// this interface will allow for specialization of staring end ending points' type
public interface IShape<TPoint> : IShape
{
// note 'new' will be required here most probably, I didn't compile it
TPoint Start { get; set; }
TPoint End { get; set; }
}
// a line will be a shape descibed by a pair of points; for the sake of
public interface ILine : IShape<Point> { }
// a curve will be a shape described by a pair of control points
public interface ICurve : IShape<ControlPoint> { }
Then, the actual implementations of ILine
and ICurve
can use explicit implementations of the Start
and End
properties coming from the non-generic IShape
interface. This will favor strongly-typed access to the delimiting points, while preserving the ability to work with them as with plain IShape
s.
public class Curve : ICurve
{
public ControlPoint Start { get; set; }
public ControlPoint End { get; set; }
IShape.Start
{
get { return Start; }
set
{
if (!(value is ControlPoint)) ... some error handling ...;
Start = (ControlPoint)value;
}
}
IShape.End { ... similarly as IShape.Start ... }
}
public class Line : ILine { ... similarly as Curve ... }
Note that in respect to the possible error scenario shown above an improvement could be limiting IShape
s properties to getters only. Or, the setter passing the value
into the strongly-typed version of Start
or End
could do some kind of conversion of a point into a control point. The right solution is domain-specific of course.