George R
George R

Reputation: 3860

Interface implementation overrides, etc

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

Answers (4)

Ondrej Tucny
Ondrej Tucny

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 ControlPoints to describe its start and end, while the line uses plain Points 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 IShapes.

 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 IShapes 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

David Culp
David Culp

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

Timwi
Timwi

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

Jon Hanna
Jon Hanna

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

Related Questions