KnigaRead.com/

W Cat - Описание языка PascalABC.NET

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн W Cat, "Описание языка PascalABC.NET" бесплатно, без регистрации.
Перейти на страницу:

PitPixel(x,y,Color.Black);

end;

end;

Использование override при переопределении абстрактных методов обязательно, поскольку абстрактные методы являются разновидностью виртуальных.

Перегрузка операций

Перегрузка операций - это средство языка, позволяющее вводить операции над типами, определяемыми пользователем. В PascalABC.NET можно использовать только предопределенные значки операций. Перегрузка операций для типа T, являющегося классом или записью, осуществляется при помощи статической (классовой) функции-метода со специальным именем operator ЗнакОперации. Перегрузка специальных операций +=, -=, *=, /= осуществляется с помощью статической (классовой) процедуры-метода, первый параметр которой передается по ссылке.

Например:

type

Complex = record

re,im: real;

class function operator+(a,b: Complex): Complex;

begin

Result.re := a.re + b.re;

Result.im := a.im + b.im;

end;

class function operator=(a,b: Complex): boolean;

begin

Result := (a.re = b.re) and (a.im = b.im);

end;

end;

Для перегрузки операций действуют следующие правила:

* Перегружать можно все операции за исключением @ (взятие адреса), as, is, new. Кроме того, можно перегружать специальные бинарные операции +=, -=, *=, /=, не возвращающие значений.

* Перегружать можно только еще не перегруженные операции.

* Тип по крайней мере одного операнда должен совпадать с типом класса или записи, внутри которого определена операция.

* Перегрузка осуществляется с помощью статической функции-метода, количество параметров которой совпадает с количеством параметров соответствующей операции (2 - для бинарной, 1 - для унарной).

* Перегрузка операций +=, -=, *=, /= для соответствующих операторов осуществляется с помощью статической процедуры-метода, первый параметр которой передается по ссылке и имеет тип записи или класса, в котором определяется данная операция, второй - передается по значению и совместим по присваиванию с первым. Перегрузка остальных операций осуществляется с помощью статических функций-методов.

* Типы интерфейсов не могут быть типами параметров. Причина: типы параметров должны вычисляться на этапе компиляции.

* Операции приведения типа задаются статическими функциями, у которых вместо имени используется operator implicit (для неявного приведения типа) или operator explicit (для явного приведения типа).

Например:

type

Complex = record

...

class function operator implicit(d: real): Complex;

begin

Result.re := d;

Result.im := 0;

end;

class function operator explicit(c: Complex): string;

begin

Result := Format('({0},{1})',c.re,c.im);

end;

class procedure operator+=(var c: Complex; value: Complex);

begin

c.re += value.re;

c.im += value.im;

end;

class function operator+(c,c1: Complex): Complex;

begin

Result.re := c.re + c1.re;

Result.im := c.im + c1.im;

end;

end;

Можно перегружать операции с помощью методов расширения - в этом случае при описании подпрограммы не писать слово class. Например, так в системном модуле реализовано добавление числа к строке:

function string.operator+(str: string; n: integer): string;

begin

result := str + n.ToString;

end;

Классовые поля, методы и конструкторы

В классе можно объявить так называемые классовые (статические) поля и методы. Они не принадлежат конкретному экземпляру класса, а связаны с классом. Для их вызова используется точечная нотация, причем, перед точкой используется не имя объекта, а имя класса. Чтобы поле или метод сделать классовым (статическим), перед его именем следует указать ключевое слово class.

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

type

Person = class

private

name: string;

age: integer;

class cnt: integer := 0;

public

constructor (n: string; a: integer);

begin

cnt += 1;

name := n;

age := a;

end;

class function Count: integer;

begin

Result := cnt;

end;

end;

begin

var p: Person := new Person('Иванов',20);

var p1: Person := new Person('Петров',18);

writeln(Person.Count); // обращение к классовому методу Count

end.

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

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

Например, определим в классе Person классовое поле - массив объектов типа Person - и инициализируем его в классовом конструкторе. Потом указанный массив можно использовать в реализации классовой функции RandomPerson, возвращающей случайный объект типа Person:

type

Person = class

private

class arr: array of Person;

name: string;

age: integer;

public

class constructor;

begin

SetLength(arr,3);

arr[0] := new Person('Иванов',20);

arr[1] := new Person('Петрова',19);

arr[2] := new Person('Попов',35);

end;

//...

class function RandomPerson: Person;

begin

Result := arr[Random(3)];

end;

end;

const cnt = 10;

begin

var a := new Person[cnt];

for var i:=0 to a.Length-1 do

a[i] := Person.RandomPerson;

end.

Методы расширения

Любой существующий класс, хранящийся во внешней dll, и все классы стандартной библитеки .NET можно расширить новыми методами. Такие методы расширения отличаются от обычных подпрограмм тем. что перед именем подпрограммы ставится имя расширяемого класса с точкой. Например:

procedure integer.Print;

begin

write(Self)

end;

begin

var i := 1;

i.Print;

end.

Можно расширить интерфейс, тогда все классы, реализующие этот интерфейс, получат этот метод. Например, в системном модуле PABCSystem так расширен стандартный интерфейс IEnumerable<T> методом Print:

function System.Collections.Generic.IEnumerable<T>.Print(): IEnumerable<T>;

begin

var g := Self.GetEnumerator();

if g.MoveNext() then

write(g.Current);

while g.MoveNext() do

write(' ', g.Current);

Result := Self;

end;

В результате все классы, реализующие интерфейс IEnumerable<T>, расширяются методом Print:

function System.Collections.Generic.IEnumerable<T>.Print(): IEnumerable<T>;

begin

var g := Self.GetEnumerator();

if g.MoveNext() then

write(g.Current);

while g.MoveNext() do

write(' ', g.Current);

Result := Self;

end;

С помощью методов расширения можно перегружать операции.

Для методов расширения имеется ряд ограничений:

* Методы расширения не могут быть виртуальными.

* Если метод расширения имеет то же имя, что и обычный метод, то предпочте5ние отдаётся обычному методу.

Анонимные классы

Иногда необходимо сгенерировать объект класса на лету, не описывая класс. У такого класса нет имени (он анонимный), но известен набор полей.

Объект анонимного класса создаётся следующим образом:

var p := new class(Name := 'Иванов', Age := 20);

Println(p.Name,p.Age);

У объекта p автоматически генерируются публичные поля Name и Age соответствующих типов.

Два объекта принадлежат к одному анонимному классу если они имеют одинаковый набор полей, и эти поля принадлежат к одинаковым типам. Например:

var p1 := new class(Name := 'Петров', Age := 21);

p1 := p;

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

var Name := 'Попова';

var Age := 23;

var p := new class(Name, Age);

Println(p.Name,p.Age);

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

var d := new DateTime(2015,5,15);

var p := new class(d.Day, d.Month, d.Year);

Println(p.Day, p.Month, p.Year);

Println(p);

Автоклассы

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*