Наследовать интерфейс, реализовать часть методов, позволить производному классу реализовать остальные
Определите следующий интерфейс С#:
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. Спасибо заранее!
Ответы
Ответ 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
.