C#. Windows Forms. Реализация концепций «наследования» и «полиморфизма» в объектно-ориентированном программировании на примере создания двух классов. Интерфейсы ICloneable, IEquatable

Реализация концепций «наследования» и «полиморфизма» в объектно-ориентированном программировании на примере создания двух классов. Интерфейсы ICloneable, IEquatable

 

Содержание


Поиск на других ресурсах:

 

Условие задачи

В разработанных классах необходимо реализовать такие концепции ООП как «наследование» и «полиморфизм«.

Нужно описать базовый класс (класс-предок) и унаследовать (наследовать) от него. Часть функциональности классов нужно реализовать с использованием свойств.

Каждый класс должен содержать реализацию по крайней мере:

  • двух конструкторов;
  • двух методов;
  • двух свойств;
  • двух интерфейсов.

Необходимо переопределить операции над объектами классов. Создать класс, который определяет окружность на плоскости. Используя разработанные классы найти площадь круга.

Реализовать интерфейсы ICloneable, IEquatable.

⇑


Математическая постановка задачи

На рисунке 1 изображена окружность на плоскости.

 

окружность плоскость рисунок

Рис. 1. Изображение окружности на плоскости

Как видно из рисунка 1, для обозначения окружности на плоскости нужно иметь:

  • координаты (x, y) точки середины окружности;
  • радиус окружности R.

Площадь окружности исчисляется по формуле:

Площадь окружности Формулагде S — площадь окружности; R — радиус окружности; π– константа, которая равняется 3.1415.

  ⇑


Выполнение

1. Создание нового проекта.

Запустить Microsoft Visual Studio. Создать новый проект как Windows Forms Application. Пример создания нового проекта приведен здесь.

Автоматически будет создана новая форма приложения.

  ⇑


2. Размещение элементов управления на форме.

Разместить на форме следующие элементы управления (рисунок 2):

  • два элемента управления типа Label. Автоматически будут созданы объекты с именами label1 и label2;
  • элемент управления типа TextBox. Создается новый объект с именем textBox1;
  • элемент управления типа Button. Создается объект с именем button1.

 

C# Windows Forms Размещение элемент управления форма

Рис. 2. Размещение элементов управления на форме

  ⇑


3. Настройка свойств элементов управления.

Настроить следующие свойства элементов управления (рисунок 3):

  • в элементе управления label1 свойство Text = «R = «;
  • в элементе управления label2 свойство Text = «S = «;
  • в элементе управления button1 свойство Text = «Вычислить«;
  • в элементе управления Form1 свойство Text = «Площадь круга«.

 

C# Windows Forms Форма приложения

Рис. 3. Форма приложения после настройки элементов управления

После настройки формы нужно заняться созданием классов.

  ⇑


4. Классы, необходимые для решения задачи.

В соответствии с условием задачи, нужно создать два класса:

  • класс Point, отображающий точку на плоскости. Этот класс должен быть базовым;
  • класс Circle, отображающий круг. Этот класс должен унаследовать данные и методы класса Point (в соответствии с условием задачи).

В данной задаче класс Point называется базовым, а класс Circle – производным.

Итак, возникает иерархия с 2-х классов. Схема иерархии классов и отображение данных (полей) в классах изображенная на рисунке 4.

C# Иерархия классов рисунок

Рис. 4. Иерархия классов в соответствии с условием задачи

По ходу решения задачи в классы будут вноситься изменения: свойства и методы.

Опишем класс Point, содержащий только данные. На языке C# листинг класса Point будет следующий:

class Point
{
    // поля класса объявлены как protected
    protected float x; // координата x точки
    protected float y; // координата y точки
}

В классе Point данные класса описываются как protected. Это означает, что эти данные есть видимыми в производных классах и невидимыми извне (скрытые для доступа через объект класса).

Для доступа к данным будут использоваться свойства и специальные методы.

Таким же образом создается класс Circle. Пока что листинг класса Circle следующий:

class Circle:Point // наследует данные из класса Point
{
    // поля класса
    private float R; // радиус окружности
}

В описании класса Circle, начало строки

class Circle:Point

означает, что класс Circle наследует данные и методы класса Point.

Таким образом, на данный момент, в классе Circle есть доступными три поля:

  • поле x из унаследованного класса Point;
  • поле y из унаследованного класса Point;
  • поле R, которое есть внутренним полем класса Circle.

  ⇑


5. Реализация конструкторов класса Point.

В соответствии с условием задачи, каждый из классов должен содержать минимум два конструктора.

Последовательно в текст класса Point нужно добавить два конструктора. Первый конструктор без параметров инициализирует данные нулевыми значениями.

Второй конструктор – параметризованный. Этот конструктор получает входящими 2 параметра и устанавливает новые значения для внутренних переменных x и y.

Ниже приведен текст программного кода обоих конструкторов:

// конструктор 1 - без параметров
public Point()
{
    x = 0f; // обнуление значений координат
    y = 0f;
}

// конструктор 2 - с двумя параметрами
public Point(float nx, float ny)
{
    x = nx;
    y = ny;
}

Следует обратить внимание, что конструкторы имеют тип доступа public, так как они вызываются извне при создании объекта класса.

  ⇑


6. Реализация конструкторов класса Circle.

Таким же образом нужно добавить два конструктора в класс Circle.

// конструктор 1 - инициализирует поля нулевыми значениями
public Circle()
{
    x = 0f; // 0f - означает, что это значение имеет тип float
    y = 0f;
    R = 0f;
}

// конструктор 2 - с тремя параметрами
public Circle(float nx, float ny, float nR)
{
    x = nx;
    y = ny;
    R = nR;
}

  ⇑


7. Реализация методов класса Point.

В соответствии с условием задачи, в каждом классе должны быть реализованы два метода. Реализуем методы, которые выполняют следующие действия:

  • метод GetPoint(), возвращающий точку типа Point;
  • метод SetXY(), устанавливающий новые значения внутренних переменных класса Point.

Тип доступа для методов выбран как public.



Листинг методов следующий.

public Point GetPoint()
{
    Point p; // описывается переменная (объект), для которой память еще не выделена
    p = new Point(x,y); // выделяется память, создается новый экземпляр класса
    return p;
}

// метод, который устанавливает новые значения внутренних переменных
public void SetXY(float nx, float ny)
{
    x = nx;
    y = ny;
}

  ⇑


8. Реализация методов класса Circle.

Подобным образом создаются методы класса Circle. В классе Circle реализуются два метода. Первый метод GetCircle() возвращает объект (с выделением памяти) типа Circle.

Второй метод SetXYR() устанавливает новые значения внутренних переменных. Этот метод получает входными 3 параметра (координаты x, y и радиус R окружности). В методе продемонстрировано использование ключевого слова this для доступа к полям класса. В этом случае имена локальных переменных-параметров совпадают с именами полей класса и скрывают их видимость внутри метода.

Листинг методов следующий:

// методы класса Circle в соответствии с условием задачи
// метод, который получает новый экземпляр класса типа Circle
public Circle GetCircle()
{
    Circle c;
    c = new Circle(x, y, R);
    return c;
}

// метод, который устанавливает новые значения во внутренние поля класса Circle

public void SetXYR(float x, float y, float R)
{
    // пример использования ключевого слова this
    // во внутреннюю переменную x класса заносится значение локальной переменной x метода SetXYR()
    this.x = x;
    // так же заполняются внутренние переменные y и R класса Circle
    this.y = y;
    this.R = R;
}

  ⇑


9. Примеры использования конструкторов и методов классов Point и Circle.

Приведем несколько примеров использования конструкторов и методов классов Point и Circle.

Демонстрация методов GetPoint() и SetXY():

// класс Point
// вызов конструктора без параметров класса Point()
Point p = new Point();
Point p1 = null, p2 = null;
p1 = new Point(4.5f, 3.2f); // вызов конструктора с двумя параметрами
p2 = p1.GetPoint();
p2.SetXY(3.3f, -2.8f); // установить новые значения

// класс Circle
Circle c = new Circle(); // конструктор без параметров
Circle c1, c2;
c1 = new Circle(3.3f, -2.8f, 12f); // выделение памяти
c2 = c1.GetCircle(); // метод GetCircle()
c2.SetXYR(3.8f, -1.9f, 8f); // метод SetXYR()

  ⇑


10. Исходный код классов.

На данный момент классы Point и Circle имеют следующий вид:

// описание класса Point
class Point
{
    // поля класса объявлены как protected
    protected float x; // координата x точки
    protected float y; // координата y точки

    // конструктор 1 - без параметров
    public Point()
    {
        x = 0f;
        y = 0f;
    }

    // конструктор 2 - с двумя параметрами
    public Point(float nx, float ny)
    {
        x = nx;
        y = ny;
    }

    public Point GetPoint()
    {
        Point p; // описывается переменная (объект), для которой память еще не выделена
        p = new Point(x,y); // выделяется память, создается новый экземпляр класса
        return p;
    }

    // метод, который устанавливает новые значения внутренних переменных
    public void SetXY(float nx, float ny)
    {
        x = nx;
        y = ny;
    }
}

// описание класса Circle
class Circle:Point // наследует данные класса Point
{
    // поля класса
    private float R; // радиус окружности
    // конструктор 1 - инициализирует поля нулевыми значениями
    public Circle()
    {
        x = 0f;
        y = 0f;
        R = 0f;
    }

    // конструктор 2 - с тремя параметрами
    public Circle(float nx, float ny, float nR)
    {
        x = nx;
        y = ny;
        R = nR;
    }

    // методы класса Circle в соответствии с условием задачи
    // метод, который получает новый экземпляр класса типа Circle
    public Circle GetCircle()
    {
        Circle c; // создается новый объект типа класс Circle
        c = new Circle(x, y, R); // выделяется память для объекта c, создается экземпляр класса
        return c;
    }

    // метод, который устанавливает новые значения во внутренние поля класса Circle
    public void SetXYR(float x, float y, float R)
    {
        // пример использования ключевого слова this
        this.x = x; // во внутреннюю переменную x класса заносится значение локальной переменной x метода SetXYR()
        this.y = y; // так же заполняются внутренние переменные y и R класса Circle
        this.R = R;
    }
}

  ⇑


11. Реализация свойств класса Point.

Следующим шагом нужно создать два свойства класса Point.

Первое свойство возвращает или устанавливает значение координат точки x. Второе свойство возвращает/устанавливает значение координаты y.

Имя первого свойства XX. Имя второго свойства YY.

Листинг свойств XX и YY следующий:

// свойство, которое возвращает/устанавливает координату x
public float XX
{
    get { return x; }
    set { x = value; }
}

// свойство, которое возвращает/устанавливает координату y
public float YY
{
    get { return y; }
    set { y = value; }
}

После такого описания свойства XX и YY можно использовать. Далее приведен пример использования свойств XX и YY.

...

Point p = new Point(5.2f, 6.35f); // создание объекта типа Point
float x,y;

x = p.XX; // x = 5.2
y = p.YY; // y = 6.35

// переопределить значения
p.XX = 2.33f;
p.YY = -3.8f;

x = p.XX; // x = 2.33
y = p.YY; // y = -3.8

...

  ⇑


12. Реализация свойств класса Circle.

Аналогичным образом описываются свойства класса Circle. Первое свойство читает/устанавливает значение радиуса R. Второе свойство читает/устанавливает значение точки, которая есть центром кола (класс Point).

// свойства класса Circle
// свойство, которое возвращает значение радиуса окружности R
public float RR
{
    get { return R; }
    set { R = value; }
}

// свойство, которое возвращает значение точки типа Point, которая есть центром окружности
public Point PP
{
    get
    {
        Point p;
        p = new Point(x, y);
        return p;
    }

    set
    {
        x = value.XX;
        y = value.YY;
    }
}

В нижеследующем листинге описывается пример использования свойств PP и RR.

// создание объекта, в котором значение полей x = 5.5; y = -2.3; R = 12.8
Circle c = new Circle(5.5f, -2.3f, 12.8f);
float x, y, R;

// доступ к свойству PP класса Circle
x = c.PP.XX; // x = 5.5
y = c.PP.YY; // y = -2.3

// доступ к свойству RR
R = c.RR; // R = 12.8
c.RR = -20.85f;

  ⇑


13. Реализация методов интерфейса ICloneable в классах.

Если реализовать интерфейс ICloneable, то можно обеспечить глубокое копирование объектов.

В нижеследующем примере осуществляется поверхностное копирование объектов:

Point p1 = new Point(5.3f, -6.8f);
Point p2;

// поверхностное копирование
p2 = p1; // оба объекта указывают на один участок памяти

Интерфейс ICloneable представляет один метод для реализации. Этот метод называется Clone(). Этот метод нужно реализовать в классах Point и Circle. Реализация метода работает по одинаковому принципу.

Прежде всего нужно изменить заголовок класса Point. Вместо текста

class Point

нужно набрать

class Point:ICloneable

Это означает, что в классе Point нужно реализовать единственный метод Clone(). Листинг метода следующий:

// реализация метода Clone() в интерфейсе ICloneable
public object Clone()
{
    Point p = new Point(x, y);
    return p;
}

Так как класс Circle наследует класс Point, который наследует интерфейс ICloneable, то в классе Circle также можно реализовать метод Clone() (но не обязательно).

// Реализация интерфейса ICloneable в классе Circle
public object Clone()
{
    Circle c = new Circle();
    return c;
}

Пример использования метода Clone().

Point p1 = new Point(5.2f, 6.35f); // создание объекта
Point p2;

// глубокое копирование, так как реализован метод Clone()
p2 = (Point)p1.Clone();// для p2 отдельно выделяется память

// класс Circle
Circle c1 = new Circle(3.2f, -8.3f, 11f);
Circle c2 = null;
c2 = (Circle)c1.Clone(); // для c2 отдельно выделяется оперативная память

  ⇑


14. Реализация методов интерфейса IEquatable.

Интерфейс IEquatable реализуется в тех классах, где нужно определить порядок сравнения двух объектов на равенство их значений. В этом интерфейсе определяется только один метод Equals(). Этот метод возвращает значение true, если значение вызывающего объекта есть равным значению другого объекта, который является параметром в методе Equals().

Для класса Point реализуем метод Equals(), который сравнивает на равенство значения переменных x и y.

Листинг метода Equals() для класса Point следующий:

// реализация метода Equals() из интерфейса IEquatable
public bool Equals(Point p2)
{
    if ((this.x == p2.x) && (this.y == p2.y))
        return true;
    else
        return false;
}

Листинг метода Equals() для класса Circle следующий:

// Реализация метода Equals() из интерфейса IEquatable
public bool Equals(Circle c2)
{
    if ((this.x == c2.x) && (this.y == c2.y) && (this.R == c2.R))
        return true;
    else
        return false;
}

В программе метод Equals() для классов можно использовать следующим образом:

// демонстрация метода Equals() для класса Point
Point p1 = new Point(3f, -5.5f);
Point p2;
bool f;

p2 = (Point)p1.Clone();
f = p1.Equals(p2); // f = true

// демонстрация метода Equals() для класса Circle
Circle c1 = new Circle(3.2f, -8.3f, 11f);
Circle c2 = new Circle(3.2f, -8.3f, 11f);
f = c1.Equals(c2); // f = true

  ⇑


15. Реализация метода вычисления площади круга в классе Circle.

Название метода – GetSquare(). Для вычисления площади круга нужно иметь только радиус R. Радиус получается с внутренней переменной R. Результат вычисления есть тип float.

В тексте класса Circle нужно добавить следующий метод:

// Метод, который вычисляет площадь круга
public float GetSquare()
{
    const float pi = 3.1415f;
    float s;
    s = pi * R * R;
    return s;
}

  ⇑


16. Сокращенный листинг класса Point.

Итак, создание классов завершено.

Сокращенный листинг класса Point следующий (детали реализации описываются в предыдущих пунктах):

class Point:ICloneable,IEquatable<Point>
{
    // поля класса объявленные как protected
    protected float x; // координата x точки
    protected float y; // координата y точки

    // конструктор 1 - без параметров
    public Point() { ... }

    // конструктор 2 - с двумя параметрами
    public Point(float nx, float ny) { ... }

    // метод, который возвращает точку с координатами
    public Point GetPoint() { ... }

    // метод, который устанавливает новые значения внутренних переменных
    public void SetXY(float nx, float ny) { ... }

    // свойство, которое возвращает/устанавливает координату x
    public float XX
    {
        get { ... }
        set { ... }
    }

    // свойство, которое возвращает/устанавливает координату y
    public float YY
    {
        get { ... }
        set { ... }
    }

    // реализация метода Clone() в интерфейсе ICloneable
    public object Clone() { ... }

    // реализация метода Equals() из интерфейса IEquatable
    public bool Equals(Point p2) { ... }
}

  ⇑


17. Сокращенный листинг класса Circle.

Низшее приведен листинг класса Circle, в котором скрыты детали реализации. Реализация методов, свойств, конструкторов описывается в предыдущих пунктах.

class Circle:Point // наследует данные класса Point
{
    // поля класса
    private float R; // радиус окружности

    // конструктор 1 - инициализирует поля нулевыми значениями
    public Circle() { ... }

    // конструктор 2 - с тремя параметрами
    public Circle(float nx, float ny, float nR) { ... }

    // методы класса Circle в соответствии с условием задачи
    // метод, который получает новый экземпляр класса типа Circle
    public Circle GetCircle() { ... }

    // метод, который устанавливает новые значения во внутренние поля класса Circle
    public void SetXYR(float x, float y, float R) { ... }

    // свойства класса Circle
    // свойство, которое возвращает значение радиуса окружности R
    public float RR
    {
        get { ... }
        set { ... }
    }

    // свойство, которое возвращает значение точки типа Point, которая есть центром окружности
    public Point PP
    {
        get { ... }
        set { ... }
    }

    // Реализация интерфейса ICloneable
    public object Clone() { ... }

    // Реализация метода Equals() из интерфейса IEquatable
    public bool Equals(Circle c2) { ... }

    // Метод, который вычисляет площадь круга
    public float GetSquare() { ... }
}

  ⇑


18. Программирование обработчика события клика на кнопке «Вычислить«.

Вычисление площади круга осуществляется в момент, когда пользователь сделает клик на кнопке «Вычислить«. Листинг обработчика события клика на кнопке «Вычислить» следующий:

private void button1_Click(object sender, EventArgs e)
{
    Circle c = new Circle();
    float s;

    // заполнить значения R
    c.RR = (float)Double.Parse(textBox1.Text);

    // вызвать метод определения площади круга
    s = c.GetSquare();
    label2.Text = "S = " + s.ToString();
}

Результат работы программы изображен на рисунке 5.

C# Windows Forms Выполнение программы

Рис. 5. Выполнение программы

  ⇑


Связанные темы

  ⇑