Reputation: 3860
Here's the simplest form of my question:
IApple requires, among other things, property Flavor IToffeeApple also requires property Flavor
The problem is, I want IToffeeApple to implement IApple (public interface IToffeeApple : IApple), but they both have the same property requirement. This becomes a problem when, for 1 purpose I need a Collection of Sweets (only IToffeeApple) where IToffeeApple can also be recognised as IApple.
Is it ok to use the "new" keyword in my interface, so that any implementers have 2 Flavor properties?
Have I explained myself poorly? :\
edit: I have. The actual context is geometry:
ICurve is just extra functionality on top of ILine, yet it means I want to return the Start and End as IControlPoint rather than IPoint, so I either implement both and have a return of both IControPoint and IPoint of Start and End, or I just ignore IPoint/ILine and throw DRY out the window.
Upvotes: 0
Views: 173
Reputation: 27964
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.
Upvotes: 3
Reputation: 5480
Try something along the lines of:
interface IFlavor; interface ISweets: IFlavor; interface IApple: IFlavor; interface IToffeeApple: IApple, ISweets;
IEnumerable<ISweets>
can hold the IToffeeApple, but not IApple.
When there seems to be a need to 'replace' an inherited property for the inheritance make sense I generally look for one of two things. Either the inheritance is forced (a car and an apple both have a color, but are not often thought of as polymorphic), or the inheritance is deeper than it seemed at first glance.
If I understand the example well enough, deeper inheritance fits well enough.
To fit your new example:
public interface IPoint
{}
public interface IControlPoint : IPoint
{
// added functionality of IControlPoint
}
public interface ILine<TPoint>
where TPoint : IPoint
{
TPoint Start { get; set; }
TPoint End { get; set; }
}
public interface ICurve<TPoint> : ILine<TPoint>
where TPoint : IPoint
{
// added functionality of ICurve
}
I am making the assumption that IControlPoint
implements IPoint
, but it seemed reasonable.
Basically, generics takes care of ICurve
needing an IControlPoint
to work with, while ILine
needs an IPoint
.
Upvotes: 3
Reputation: 66573
No, you don’t need the new
keyword to inherit a property from an interface in another interface:
public interface IApple
{
Flavor Flavor { get; }
}
public interface IToffeeApple : IApple
{
ICollection<Sweet> Sweets { get; }
}
public class MyToffeeApple : IToffeeApple
{
public Flavor Flavor { get { return Flavors.ToffeeFlavor; } }
public ICollection<Sweet> Sweets { get { return new Sweet[0]; } }
}
Works just fine. If this doesn’t answer your question, please edit the question to include detail that explains in what way the above is insufficient.
Upvotes: 1
Reputation: 113272
No, because IToffeeApple is only inheriting from IApple (it isn't implementing anything, it's an interface) and there's no conflict; just leave the Flavour property out so it inherits it from IApple.
This doesn't becomes a property with a collection that only has IToffeeApple, because there won't be any conflicting cases there either.
It could only really become a problem if something implemented both IToffeeApple, and either something else where flavour had a different type, or IQuark where flavour has a different meaning. In this case the class implementing them should implement on or both of the properties explicitly.
Upvotes: 0