11

Define the following C# interface:

public interface IShape
{
    int NumberOfLineSegments {get;}
    int Area {get;}
}

Next, I want to define several rectangle classes: Trapezoid, square,etc. All of these classes differ in their Area() property, but NumberOfLineSegments() always returns 4. Therefore, I would like an 'interim' class or interface, called Rectangle (or IRectangle), that looks something like:

public Rectangle : IShape
{
    public int NumberOfLineSegments{get{return 4;}}
}

I want Rectangle to implement only NumberOfLineSegment(), and leave it to its derived classes to implement the rest:

public Square : Rectangle 
{
    public int Area() {get{return length*height;}
}

However, since IShape is an interface, the Rectangle class must implement also Area(), which it knows not how to implement.. Thus I seem to be stuck, either with defining a 'dummy' Area() method for Rectangle, or not using inheritence at all.

Is there a way to circumvent this? I have read extensively through Richter's clr via c#, and in StackOverflow. Thanks in advance!

zuuz
  • 859
  • 1
  • 12
  • 23
  • It's a good idea to make the class `abstract` as others have suggested. But for the naming, as a geometer, I think you should consider calling your class `Quadrilateral` as that's what a general four-sided polygon (or tetragon) is called. – Jeppe Stig Nielsen Jul 17 '12 at 13:57
  • Accepted. Not a native English speaker.. – zuuz Jul 17 '12 at 14:57

6 Answers6

7

Rectangle class should be abstract and define Area() method as abstract.

public interface IShape
{
    int NumberOfLineSegments {get;}
    float Area{get;}
}

public abstract class RectangleBase : IShape
{
    public int NumberOfLineSegments { get { return 4; } }

    public abstract float Area { get; }
}

public sealed class Square : RectangleBase
{
    public override fload Area() { get { return length*height; }
}

And if you need Rectangle instances:

public sealed class Rectangle : ReectangleBase
{
    public int NumberOfLineSegments { get { return 4; } }

    public float Area { get { throw new NotImplementedException(); } }
}
Adam Houldsworth
  • 63,413
  • 11
  • 150
  • 187
sll
  • 61,540
  • 22
  • 104
  • 156
  • BTW, if you plan you career or future anyhow related to programming/software development - I would suggest reading basics of OOP – sll Jul 17 '12 at 13:56
5

There are two options.

  1. Make the implementation virtual and empty (or throw a NotImplementedException), so it does nothing by default until derived.
  2. Make the base class abstract and create abstract signatures for the interface methods you want to force down the chain.

Number 2 is much more preferable, as it forces derived classes to implement the method, whereas in number 1 derived classes are not forced to override base virtual methods.

Abstract methods can successfully satisfy interface definitions as the compiler will know that abstract classes themselves cannot be instantiated, and any derived classes are forced to have the abstract method implemented.

That said, if there are interface members that do not make sense to a particular type, it is usually an indicator to break down your interfaces:

public interface IShape : ICalculateArea, IHaveLineSegments
{
}

public interface ICalculateArea
{
    float Area { get; }
}

public interface IHaveLineSegments
{
    int NumberOfLineSegments { get; }
}

class Rectangle : IHaveLineSegments
{
    public int NumberOfLineSegments { get; private set; }
}

class Square : Rectangle, IShape
{
    public float Area { get; private set; }
}
Adam Houldsworth
  • 63,413
  • 11
  • 150
  • 187
  • BTW, [Public Virtual Methods: A Bad Idea](http://www.spatial.com/blog/public-virtual-methods-bad-idea), [Is the Non-Virtual Interface (NVI) idiom as useful in C# as in C++?](http://stackoverflow.com/q/2340487/485076) – sll Jul 17 '12 at 13:58
  • @sll Possibly, though I'd say that is opinionated. It is possible to derive the contract of a service in a derived class and still maintain good code and maintain the meaning of the contract - after all, someone doing that is trying to achieve the DRY principle. Good points though. – Adam Houldsworth Jul 17 '12 at 14:00
  • @sll I'd suggest putting this advisory on your own answer too, if you feel it is worth putting it on mine. – Adam Houldsworth Jul 17 '12 at 14:18
  • When deciding whether to split interfaces, I think it's important to consider whether it would make sense to have code sometimes hold a reference to something that can't perform a particular function but at other times, when the reference happens to identify an instance that can perform that function, make use of that ability. When such usage is helpful, holding a reference to an interface type whose members aren't usable on all instances may be more helpful than having to hold a reference of a less specific interface type and cast it to the specific type when its abilities are needed. – supercat Dec 05 '13 at 23:50
1

define the method as abstract.

public abstract float Area{get;}
Jauco
  • 1,320
  • 13
  • 23
1

Use an abstract class which implements the interface:

public abstract class Rectangle : IShape {
    public int NumberOfLineSegments { get { return 4; } }
    public abstract float Area { get; }
}

Your particular rectangle classes then simply inherit from the Rectangle abstract class.

Lucas Jones
  • 19,767
  • 8
  • 75
  • 88
0

This be ok? Forces implementation in derived classes.

public abstract class Rectangle : IShape
{
    NumberOfLineSegments{get{return 4;}}
    abstract float Area { get; }
}
Paddy
  • 33,309
  • 15
  • 79
  • 114
0

Personally I prefer the @sll's solution (and the others which are essentially the same), but for the record there's one more way:

public interface IShape
{
    int NumberOfLineSegments {get;}
    float Area{get;}
}

public class Rectangle
{
    public int NumberOfLineSegments { get { return 4; } }
}

public sealed class Square : Rectangle, IShape
{
    public float Area() { get { return length*height; }
}

This way you can spare abstract class, with a cost of defining a rigid superclass (so you would be unable to have different superclass for the Square).

Note that this works despite of the fact that Rectangle does not implement IShape.

Vlad
  • 35,022
  • 6
  • 77
  • 199
  • Justa comment, personally I preffer clearly markign intension to make a class as a base, so all classes which are designed to be a base class should shoudl be marked `absract` so you will not find yourself ran into situation when someone instantiated a class, someone inherited from it and in future it would be mess extending such a class or refactoring, it would be a mess – sll Jul 17 '12 at 14:03
  • @sll: yes, marking the intention is a good idea; my idea was to present a different implementation (I think not everyone expects my code to compile in the first time). All other answers seem to be on the same track: interface => abstract proto implementation => actual implementation, which requires a lot of boilerplate in proto if the interface is bigger. Making one more answer with the same idea as yours wouldn't make much sense, right? :) – Vlad Jul 17 '12 at 14:18