Подтвердить что ты не робот

Наследовать интерфейс, реализовать часть методов, позволить производному классу реализовать остальные

Определите следующий интерфейс С#:

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

Далее, я хочу определить несколько классов прямоугольников: Трапеция, квадрат и т.д. Все эти классы отличаются свойством Area(), но NumberOfLineSegments() всегда возвращает 4. Поэтому мне нужен "промежуточный" класс или интерфейс, называемый Rectangle (или IRectangle), который выглядит примерно так:

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

Я хочу, чтобы Rectangle реализовал только NumberOfLineSegment() и оставил его в своих производных классах для реализации остальных:

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

Однако, поскольку IShape является интерфейсом, класс Rectangle должен реализовывать также Area(), который не знает, как реализовать. Таким образом, я, похоже, застрял либо с определением метода 'dummy' Area() для Rectangle, либо вообще не использующего наследование.

Есть ли способ обойти это? Я читал подробно через Richter clr через С# и в StackOverflow. Спасибо заранее!

4b9b3361

Ответ 1

Есть два варианта.

  • Сделать реализацию виртуальной и пустой (или выбросить NotImplementedException), поэтому она ничего не делает по умолчанию, пока не будет получена.
  • Сделайте абстрактный базовый класс и создайте абстрактные сигнатуры для методов интерфейса, которые вы хотите навязать цепочке.

Число 2 намного предпочтительнее, поскольку оно вынуждает производные классы внедрять метод, тогда как в производных классах 1 не требуется переопределять базовые виртуальные методы.

Абстрактные методы могут успешно удовлетворять определениям интерфейсов, поскольку компилятор будет знать, что сами абстрактные классы не могут быть созданы, и любые производные классы вынуждены внедрять абстрактный метод.

Тем не менее, если есть элементы интерфейса, которые не имеют смысла для определенного типа, обычно это индикатор разбивает ваши интерфейсы:

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; }
}

Ответ 2

Rectangle класс должен быть abstract и определять Area() метод как абстрактный.

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; }
}

И если вам нужны экземпляры Rectangle:

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

    public float Area { get { throw new NotImplementedException(); } }
}

Ответ 3

определить метод как абстрактный.

public abstract float Area{get;}

Ответ 4

Используйте абстрактный класс, который реализует интерфейс:

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

Ваши конкретные классы прямоугольников затем просто наследуются от абстрактного класса Rectangle.

Ответ 5

Это нормально? Принудительная реализация в производных классах.

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

Ответ 6

Лично я предпочитаю решение @sll (и другие, которые по сути одинаковы), но для записи есть еще один способ:

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; }
}

Таким образом, вы можете сэкономить класс abstract со стоимостью определения жесткого суперкласса (чтобы у вас не было другого суперкласса для Square).

Обратите внимание, что это работает, несмотря на то, что Rectangle не реализует IShape.