Абстрактные классы и члены классов

В рассмотренном выше примере класс Figure является таким же «полноценным» классом, как Circle и Rectangle. Объект этого класса может быть создан, его методы могут быть вызваны, его поля доступны для чтения и записи. Однако класс Figure является только «оберткой» и практической надобности в создании объектов именно этого класса нет. Чтобы обезопасить дальнейшую разработку от возможных ошибок, связанных с созданием и использованием экземпляров класса Figure, в C# существует ключевое слово abstract. Ключевое слово abstract позволяет создавать классы и члены классов, которые являются неполными и должны быть реализованы в производном классе. Классы могут быть объявлены абстрактными путем помещения ключевого слова abstract перед определением класса.

abstract class Figure

{

// Координаты

public double X = 0;

public double Y = 0;

 

// Метод расчета площади фигуры

public abstract double CalcArea();

}

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

Абстрактные классы могут определять абстрактные методы. Для этого перед типом возвращаемого значения метода необходимо поместить ключевое слово abstract. Абстрактные методы не имеют реализации, поэтому определение такого метода заканчивается точкой с запятой  вместо обычного блока метода. Классы, производные от абстрактного класса, должны реализовывать все  абстрактные методы.

 

4.6. Работа со списками в C#

Классы для работы со списками, очередями, двоичными массивами, хэш-таблицами и словарями находятся в пространстве имен System.Collections.

Одним из наиболее широко используемых классов в этой коллекции является класс List. Он представляет из себя динамический список, т. е. мы можем добавлять в него элементы, удалять, искать и т. п.  Списки применяются там, где количество элементов в коллекции наперед не известно. Списки выгодно отличаются от массивов тем, что по ходу выполнения программы их размер можно изменять в любую сторону. Кроме того, в этом generic-классе реализованы многие стандартные алгоритмы (сортировка, например).

Класс List - это generic класс. Это означает, что при объявлении экземпляра этого класса указывается некий конкретный тип, который и будет содержаться в списке.

Основные методы класс List перечислены в таблице 4.1.


 

 


Таблица 4.1. Основные методы класса List

Имя Описание
Add Добавляет объект в конец коллекции List<T>
Clear Удаляет все элементы из коллекции List<T>
Contains Определяет, входит ли элемент в состав List<T>
CopyTo Перегружен. Копирует список List<T> или его часть в массив
Exists Определяет, содержит ли List<T> элементы, удовлетворяющие условиям указанного предиката
Find Выполняет поиск элемента, удовлетворяющего условиям указанного предиката, и возвращает первое найденное вхождение в пределах всего списка List<T>
FindAll Извлекает все элементы, удовлетворяющие условиям указанного предиката
Insert Добавляет элемент в список List<T> в позиции с указанным индексом
LastIndexOf Перегружен. Возвращает отсчитываемый от нуля индекс последнего вхождения значения в списке List<T> или в его части
Remove Удаляет первое вхождение указанного объекта из коллекции List<T>
RemoveAll Удаляет все элементы, удовлетворяющие условиям указанного предиката
RemoveAt Удаляет элемент списка List<T> с указанным индексом
Reverse Перегружен. Изменяет порядок элементов в списке List<T> или в его части на обратный
Sort Перегружен. Сортирует элементы в списке List<T> или в его части
ToArray Копирует элементы списка List<T> в новый массив
TrueForAll Определяет, все ли элементы списка List<T> удовлетворяют условиям указанного предиката

 

Пример использования класса List.

List<Figure> lf = new List<Figure>();

lf.Add(new Circle() {X=10,Y=10,R=100});

lf.Add(new Circle() {X=10,Y=10,R=120});

lf.Add(new Circle() {X=10,Y=10,R=130});

lf.Add(new Rectangle() {X=20,Y=20,A=50,B=60});

lf.Add(new Rectangle() {X=20,Y=20,A=60,B=50});

lf.Add(new Rectangle() {X=20,Y=20,A=30,B=30});

double sumArea = 0;

foreach (Figure f in lf)

{

sumArea += f.CalcArea();

}

Console.WriteLine("Площадь всех фигур в списке - " +

             sumArea.ToString());

Console.ReadLine();

В приведенном примере на экран будет выведено
число – сумма площадей всех фигур в списке.

Важно отметить, что обращение к методу CalcArea происходит через ссылку на класс предок Figure и, таким образом, в примере в полной мере применяется принцип полиморфизма.


Класс System.Object

В С# все типы данных (как структурные, так и ссылочные) производятся от единого общего предка: класса System.Object. Класс System.Object определяет общее полиморфное поведение для всех типов данных в.NET. Во всех предыдущих примерах не было необходимости явно указывать класс System.Object в качестве базового - это подразумевается само собой. Однако нам ничто не мешает сделать это, явно указав, что класс производится от System.Object:

class HelloClass: System.Object

// либо class HelloClass: object

{... }

Как и в любом другом классе С#, в классе System.Object существует свой набор членов (табл.4.2). Учитывая, что некоторые члены определены как виртуальные, они могут быть замещены в определении производного класса:

// Самый верхний класс в иерархии классов.NET: System.Object

namespace System

{

public class Object

{

public Object();

public virtual Boolean Equals (Object obj);

public virtual Int32 GetHashCode();

public Type GetType();

public virtual String ToString();

}

}

Таблица 4.2. Назначение методов класса System.Object

Метод Назначение
Equals() Метод сравнивает текущий объект с объектом obj, переданным в качестве параметра. По умолчанию этот метод возвращает «истинно» только тогда, когда сравниваемые сущности указывают на одну и ту же область в оперативной памяти. Поэтому этот метод в его исходной реализации предназначен только для сравнения объектов ссылочных типов, но не структурных. Для нормальной работы с объектами структурных типов этот метод необходимо заместить
GetHashCode() Возвращает целочисленное значение, идентифицирующее конкретный экземпляр объекта данного типа
GetType() Метод возвращает объект Туре(), полностью описывающий тот объект, из которого метод был вызван. Это метод идентификации времени выполнения, который предусмотрен во всех объектах
ToString() Возвращает символьное представление объекта в формате <имя_пространства_имен>.<имя_класса> (такой формат носит также название "полностью определенного имени" - fully qualified name)

 

Методы, которые типы данных наследуют от System.Object, во многих ситуациях исключительно полезны. Но обычно при создании своих собственных типов данных некоторые методы System.Object приходится замещать. Рассмотрим такое замещение на примере.

В качестве замещаемого метода выберем Object.ToString(). Мы хотим, чтобы этот метод возвращал не имя типа, а информацию о внутреннем состоянии объекта.

class Program

{

static void Main(string[] args)

{

Student s = new Student() {

FIO = "Петр Трофимов",

Age = 26

};

Console.WriteLine(s);

Console.ReadLine();

}

}

 

class Student

{

public string FIO;

public int Age;

public override string ToString()

{

return String.Format("{0}, возраст {1} лет",

      FIO, Age);

}

}

Контрольные вопросы

1. Что такое конструктор класса? Какова его роль в программе?

2. Назовите основные средства реализации принципа инкапсуляции в C#.

3. Приведите пример реализации отношения наследования в C#.

4. Каковы необходимые условия реализации принципа полиморфизма?

5. Назовите основные средства реализации принципа полиморфизма в C#.

6. Поясните роль класса System.Object в программе C#.




Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: