Последние записи
- TChromium (CEF3), сохранение изображений
- Как в Delphi XE обнулить таймер?
- Изменить цвет шрифта TextBox на форме
- Ресайз PNG без потери прозрачности
- Вывод на печать графического файла
- Взаимодействие через командную строку
- Перенести программу из Delphi в Lazarus
- Определить текущую ОС
- Автоматическая смена языка (раскладки клавиатуры)
- Сравнение языков на массивах. Часть 2
Интенсив по Python: Работа с API и фреймворками 24-26 ИЮНЯ 2022. Знаете Python, но хотите расширить свои навыки?
Slurm подготовили для вас особенный продукт! Оставить заявку по ссылке - https://slurm.club/3MeqNEk
Online-курс Java с оплатой после трудоустройства. Каждый выпускник получает предложение о работе
И зарплату на 30% выше ожидаемой, подробнее на сайте академии, ссылка - ttps://clck.ru/fCrQw
30th
Апр
Изменения в языке Дельфи 2010
Posted by bullvinkle under Журнал, Статьи
Задача предназначена для представления краткого обзора нововведений в язык Дельфи (2010) по сравнению с Дельфи 7.
Изменения в языке Дельфи 2010
Utkin
Благодаря активным попыткам компании Embecadero влиять на рынок продуктов разработки программ язык Дельфи быстро развивается, однако это развитие направлено в основном на попытки наверстать все нововведения в современных языках программирования (таких как С#). Никаких принципиально новых разработок и концепций не внедряется.
Директива Inline (появилась в Дельфи 2005)
По аналогии с С++, функции и процедуры теперь могут быть встраиваемыми со всеми вытекающими последствиями. А именно использование данной директивы не гарантирует вставку тела функции вместо ее вызова. Кроме того, существует еще целый ряд ограничений (согласно справочной системе). Эта директива бесполезна:
. при позднем связывании (virtual, dynamic, message);
. для функций и процедур имеющих ассемблерные вставки;
. для конструкторов и деструкторов, там она работать не будет (о чем Вам компилятор обязательно пожалуется);
. для главного блока программы, секций инициализации и финализации модулей;
. если метод класса обращается к членам класса с более низкой видимостью, чем сам метод. Например, если public метод обращается к private методу, то для такого метода inline-подстановка осуществляться не будет;
. для процедур и функций, которые используются в выражениях проверки условия циклов while и repeat.
Как сделать процедуру встроенной?
Procedure Add (var x: Integer; y: Integer); Inline;
Регулировать поведение inline можно следующими директивами:
{$INLINE ON} – по умолчанию включена, разрешает работу Inline;
{$INLINE AUTO} – будет осуществлена попытка встраивание кода функций и процедур, если:
а) они помечены как Inline;
б) если их размер будет менее 32-х байт.
{$INLINE OFF} – не разрешает работу Inline.
Следует отметить, что и в классическом С++ Inline никогда не была высокоэффективным механизмом, а учитывая ограничения, накладываемые компилятором Дельфи, ее использование под большим вопросом.
Перегрузка операторов (появилась в Delphi.Net)
В отличие от С++ перегрузка осуществляется немного по-другому. Для перегрузки операторов перегружается не символ оператора, а его символическое обозначение (сигнатура). Перегружать можно только для операций с экземплярами классов.
Нужно обратить внимание – TRUNC, ROUND, INC, DEC считаются операторами, а не процедурами и функциями.
Вот пример использования:
TMyClass = class
class operator Add(a, b: TMyClass): TMyClass; // Перегрузка сложение для TMyClass
class operator Subtract(a, b: TMyClass): TMyclass; // Вычитание для TMyClass
class operator Implicit(a: Integer): TMyClass; // Неявное преобразование Integer в TMyClass
class operator Implicit(a: TMyClass): Integer; // Неявное преобразование TMyClass в Integer
class operator Explicit(a: Double): TMyClass; // Явное преобразование Double в TMyClass
end;
// Пример описание сигнатуры Add для перегрузки сложения для типа TMyClass
TMyClass.Add(a, b: TMyClass): TMyClass;
begin
…
end;
var
x, y: TMyClassbegin
x := 12; // Неявное преобразование из Integer
y := x + x; // Вызов TMyClass.Add(a, b: TMyClass): TMyClass
b := b + 100; // Вызов TMyClass.Add(b, TMyClass.Implicit(100))
end;
Подробней о перегрузке операторов можно почитать здесь: http://www.realcoding.net/articles/delphinet-peregruzka-operatorov.html
Помощники класса (Class Helpers)
Интересный механизм (ответ Дельфи на расширители классов в С#), призванный решить некоторые проблемы в обход наследования. Служит для дополнения класса новыми методами и свойствами.
type
TMyClass = class
procedure MyProc;
function MyFunc: Integer;
end;
…
procedure TMyClass.MyProc;
var
X: Integer;
begin
X := MyFunc;
end;
function TMyClass.MyFunc: Integer;
begin
…
end;
…
type
TMyClassHelper = class helper for TMyClass
procedure HelloWorld;
function MyFunc: Integer;
end;
…
procedure TMyClassHelper.HelloWorld;
begin
WriteLn(Self.ClassName); // Здесь будет возвращен тип TMyClass, а не TMyClassHelper
end;
function TMyClassHelper.MyFunc: Integer;
begin
…
end;
…
var
X: TMyClass;
Begin
X := TMyClass.Create;
X.MyProc; // Вызов TMyClass.MyProc
X.HelloWorld; // Вызов TMyClassHelper.HelloWorld
X.MyFunc; // Вызов TMyClassHelper.MyFunc
end;
По сути, вариация на тему множественного наследования, но есть одна особенность – помощники класса позволяют дополнять любой существующий класс, без создания нового. Обратите внимание, что механизм помощника класса не использует явного упоминания Self при обращении к полям класса (помогаемого класса). То есть, HelloWorld имеет право обращаться к полям TMyClass (просто в нашем примере их нет). Аналогично TMyClass также имеет доступ к полям TMyClassHelper (в случае, если класс и его помощник объявлены в одном модуле).
С практической точки зрения удобный механизм, кроме одной детали – класс должен иметь только одного помощника, имеет ли он помощника проверить во время выполнения программы нельзя. Если в классе имеется несколько помощников (неважно в каком юните, лишь бы он видел класс), считаться рабочим будет только самый последний из объявленных. Это значит, что если TMyClass уже имел помощника, то будут доступны методы именно TMyClassHelper, поскольку именно он объявлен последним. Таким образом, в лучшем случае, два и более помощника для одного класса вызовут ошибку компиляции, в худшем трудно отлавливаемую ошибку, жалобы программиста на багги в IDE и компиляторе и много потерянного времени. Чем сложней проект, тем трудней будет установить причину ошибки.
С теоретической точки зрения механизм противоречивый – он увеличивает сцепляемость объектов и юнитов между собой. Перед использованием помощника, я должен проверить все модули, из которых доступен данный класс на предмет проверки существования такого помощника (представьте большой проект). Это нарушает принципы инкапсуляции – если раньше перед использованием класса нужно было знать только его интерфейс, то теперь для использования помощников, я должен отслеживать существование класса во всех модулях, где имеется доступ к данному классу. С этого момента механизм интерфейсов уже не играет особой роли, поскольку, обращаясь к объекту какого-либо класса, всегда можно обнаружить такой букет неизвестных методов, что интерфейсная часть класса становится даже вредной. Это нарушает принцип сокрытия данных – благодаря помощникам я могу менять работу своих и чужих классов и могу иметь доступ к его полям (в рамках юнита). Кстати, это ограничение на доступ к полям в рамках юнита также сводит на нет многие его плюсы – проще вписать новые методы в сам класс (или наследовать новый), чем создавать путаницу в классе, юните и проекте.
Записи стали объектами
И теперь имеют свои методы, свойства и конструкторы.
type
TMyRecord = record
type
TInnerColorType = Integer;
var
Red: Integer;
class var
Blue: Integer;
procedure printRed();
constructor Create(val: Integer);
property RedProperty: TInnerColorType read Red write Red;
class property BlueProp: TInnerColorType read Blue write Blue;
end;
constructor TMyRecord.Create(val: Integer);
begin
Red := val;
end;
procedure TMyRecord.printRed;
begin
writeln(’Red: ‘, Red);
end;
Но, сокращенная запись по-прежнему разрешена (поэтому старые проекты должны переноситься и с сокращенной формой записей).
Абстрактные классы
type
TAbstractClass = class abstract
procedure SomeProcedure;
end;
Разрешены полностью абстрактные классы (раньше допускались только конкретные методы), содержащие объявления методов для дальнейшего их перекрытия в потомках.
strict private и strict protected
Строгое private – метод или свойство для класса и невидимое никому, вне класса даже в рамках текущего юнита.
Строгое protected – методы в этой секции будут видимы самому классу и его наследникам.
Таким образом, полное объявление выглядит теперь так
type
TKlass = class(TForm)
strict protected
protected
strict private
private
public
published
automated
end;
Не наследуемые классы
По аналогии с С#, в Дельфи 2010 существуют классы от которых дальнейшее наследование невозможно:
type
TAbstractClass = class sealed
procedure SomeProcedure;
end;
Весьма сомнительное удовольствие для рядового разработчика. Никаких реальных преимуществ такой класс не дает. Точные причины создания такого механизма не известны и преимущества от его использования очень призрачны – наследование не разрушает класса предка, поэтому и запечатывать их особой необходимости нет. Считается, что запечатанные классы работают быстрей обычных (сам не проверял) и они применяются для .NET платформы (сугубо в утилитарных целях – не все обертки над низкоуровневыми операциями, такими как WinApi, можно сделать наследуемыми).
Классовые константы (возникло в Delphi
Классы могут иметь константы – сами классы, а не порождаемые от них объекты.
type
TClassWithConstant = class
public
const SomeConst = ‘This is a class constant’;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
ShowMessage(TClassWithConstant.SomeConst);
end;
Классовые типы (возникло в Delphi
Класс может теперь содержать описание типа, которое можно использовать только в пределах данного класса.
type
TClassWithClassType = class
private
type
TRecordWithinAClass = record
SomeField: string;
end;
public
class var
RecordWithinAClass: TRecordWithinAClass;
end;
…
procedure TForm1.FormCreate(Sender: TObject);
begin
TClassWithClassType.RecordWithinAClass.SomeField := ‘This is a field of a class type declaration’;
ShowMessage(TClassWithClassType.RecordWithinAClass.SomeField);
end;
Еще одно сомнительное удовольствие. Описание типа это не конкретная структура, зачем прятать его описание в тело класса?
Классовые переменные (возникло в Delphi
Класс может содержать переменные по аналогии с константами:
Type
X = class (TObject)
Public
Var
Y: Integer;
End;
Пожалуй, единственное, где это может пригодиться это работа с RTTI, вообще классы в Дельфи стали больше напоминать юниты – такие вот юниты внутри юнитов. Обратите внимание, что переменные класса могут находиться в любой секции (секции в данном случае влияют на область видимости данных переменных), тогда как поля класса не могут быть public (в Дельфи 7 могли). Применение статических полей в классе делает Дельфи все более ориентированным в сторону С# (и менее в сторону Паскаля).
Вложенные классы
Теперь классы можно объявлять внутри классов, цель избежать конфликта имен, локализовать все связанные классы между собой:
type
TOuterClass = class
strict private
MyField: Integer;
public
type
TInnerClass = class
public
MyInnerField: Integer;
procedure InnerProc;
end;
procedure OuterProc;
end;
procedure TOuterClass.TInnerClass.InnerProc;
begin
…
end;
Классы все больше перенимают концепцию модулей. Понятно, что данное нововведение дань .Net, но реальной пользы от него опять же не очень много – раньше конфликта имен избегали префиксами A и F, не могу сказать, что новый механизм дал программистам новые возможности. Также как и попытка использовать вложенные классы для складывания всего в одну большую кучу наряду с помощниками классов сильно напоминают лебедь, рак и щуку, растаскивающие Дельфи в разные стороны.
Финальные методы класса
В классах можно создавать виртуальные методы, которые перекрыть нельзя:
TAbstractClass = class abstract
public
procedure Bar; virtual;
end;
TFinalMethodClass = class(TAbstractClass)
public
procedure Bar; override; final;
end;
Переопределить «Bar» уже больше нельзя.
Статические методы класса
У классов могут быть статические методы класса – то есть методы, которые можно вызвать от типа класса. Такие методы не имеют доступа к полям класса (также как и не могут получить Self на конкретный экземпляр данного класса):
type
TMyClass = class
strict private
class var
FX: Integer;
strict protected
// Note: accessors for class properties must be declared class static.
class function GetX: Integer; static;
class procedure SetX(val: Integer); static;
public
class property X: Integer read GetX write SetX;
class procedure StatProc(s: String); static;
end;
TMyClass.X := 17;
TMyClass.StatProc(’Hello’);
Здесь же представлен пример организации свойств классов. Их использование полностью аналогично использованию переменных и констант класса.
for-element-in-collection
Теперь компилятор способен распознавать итерации в контейнерах:
for Element in ArrayExpr do Stmt;
for Element in StringExpr do Stmt;
for Element in SetExpr do Stmt;
for Element in CollectionExpr do Stmt;
Вот развернутый пример:
var
A: Array [1..6] of String;
I: String;
….
for i in A do
begin
Memo1.Lines.Add(i);
end;
Обратите внимание, что I имеет тип String это не индекс массива, а конкретные значения, которые будут получаться из массива. Кое-где конечно автоматизирует, но представьте, что мне нужно написать некий метод, в котором происходит копирование одного массива в другой. Использовать все равно придется стандартный цикл for, либо писать еще один метод – добавление элемента в массив.
Динамическая инициализация массивов
Теперь массивы получили свои конструкторы:
Type
TMas = Array of String;
Var
Mas: TMas;
Mas := TMas.Create(‘Hello’, ’World’, ’!’);
Я, конечно, не против «Create» как конструктора по умолчанию, но уже сейчас из-за отсутствия внятной русскоязычной литературы по данной теме встречаются статьи, в которых авторитетные господа пишут, что конструктор обязательно должен называться Create (речь идет не только о массивах, но также о записях и конструкторах класса). Так вот конструктор должен называться Create только для массивов. Для всех остальных имя конструктора не обязательно должно быть Create (но желательно, особенно для классов).
Дженерики
Шаблоны, они и в С++ шаблоны. Считается что первые шаблоны возникли в С++, но вообще-то они пришли из функционального программирования и правильное их название параметрический полиморфизм. Явление, когда компилятор сам вырабатывает соответствующие варианты кода на основании обобщенного алгоритма:
TList<T> = class
private
FItems: array of T;
FCount: Integer;
procedure Grow(ACapacity: Integer);
function GetItem(AIndex: Integer): T;
procedure SetItem(AIndex: Integer; AValue: T);
public
procedure Add(const AItem: T);
procedure AddRange(const AItems: array of T);
procedure RemoveAt(AIndex: Integer);
procedure Clear;
property Item[AIndex: Integer]: T read GetItem write SetItem; default;
property Count: Integer read FCount;
end;
Вот пример списка содержащего произвольные (но однотипные элементы). Тип элементов определяется на момент объявления переменной:
ilist: TList<Integer>;
То есть мы создали список целых чисел (а можно, к примеру, список строк). Дженерики удобно использовать применительно к алгоритмам контейнеров данных и комбинаторным алгоритмам. Конкретные реализации алгоритмов можно посмотреть в модуле Generics.Collections, где есть TArray, TList, TStack, TQueue, TDictionary, TObjectList, TObjectQueue, TObjectStack, TObjectDictionary и TEnumerator, способные работать с разными типами данных.
Также необходимо отметить особенность дженериков (и шаблонов в С++) – обобщенные алгоритмы экономят время программиста, сокращая только его код, но для каждого типа (для каждой комбинации типов) всегда генерируется новая версия алгоритма (поэтому размер скомпилированных программ увеличивается).
Заключение
Большинство механизмов представленных здесь:
. обеспечивают совместимость с .NET
. дань моде
. попытка угнаться за Microsoft Visual Studio
Язык не содержит принципиальных отличий и мощных механизмов, которые действительно были бы востребованы именно программистами на языке Дельфи. Все нововведения навязаны, искусственны и не всегда соответствуют концепциям ООП. Большое количество противоречивых инструментов может только запутать программистов и в течение ближайших лет можно ожидать некоторого количества критических статей в адрес языка программирования Дельфи.
Комментарий автора
Личные впечатления о среде сложились следующие: сплошные недоделки (да и в 2009-м не лучше), ждать следующую версию наверно не стоит. FrameWork идет в комплекте, ничего доустанавливать не надо. Несмотря на заявленные требования не ниже 1 гигабайта ОЗУ, у меня и при 512-ти с тормозами, но работает.
Ресурсы
. Хроники «айтишника» http://skiminog.livejournal.com/33610.html
. Общество разработчиков Embecadero http://edn.embarcadero.com
. Углубленный материал по перегрузке операторов в Дельфи http://www.realcoding.net/articles/delphinet-peregruzka-operatorov.html.
. Онлайн-перевод англоязычных материалов статьи http://www.translate.ru
Статья из второго выпуска “журнала ПРОграммистов”.
Скачать этот номер можно по ссылке.
Ознакомиться со всеми номерами журнала.
Обсудить на форуме — Изменения в языке Дельфи 2010
Похожие статьи
Купить рекламу на сайте за 1000 руб
пишите сюда - alarforum@yandex.ru
Да и по любым другим вопросам пишите на почту
пеллетные котлы
Пеллетный котел Emtas
Наши форумы по программированию:
- Форум Web программирование (веб)
- Delphi форумы
- Форумы C (Си)
- Форум .NET Frameworks (точка нет фреймворки)
- Форум Java (джава)
- Форум низкоуровневое программирование
- Форум VBA (вба)
- Форум OpenGL
- Форум DirectX
- Форум CAD проектирование
- Форум по операционным системам
- Форум Software (Софт)
- Форум Hardware (Компьютерное железо)