Подробнее об атрибутах в Delphi 2010

Malcolm Groves, “More Attributes in Delphi 2010”, public translation into Russian from English More about this translation.

Translate into another language.

В моей предыдущей статье Кратко об атрибутах в Delphi 2010, я показал основы связанные с созданием, применением и опросом атрибутов. Однако, я не привел пример, для чего вы могли бы их использовать.

Вероятно, наиболее распространенным является пример для персистентности, и, действительно, кто-то опубликовал подобный пример на сайте Wings of Wind. Я хотел бы показать другое их применение - для проверки данных (Validation).

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

Итак, с предупреждениями закончили, в таком случае я бы хотел иметь возможность добавить в мои классы метаданные указывающие правила проверки. Может быть, я хочу чтобы класс TPerson класс считался верным, когда Name - это непустое значение и значение Age лежит в диапазоне от 18 до 65. Один из способов добиться этого снабдить соответствующие свойства атрибутами, определяющими эти правила, и затем задействовать некоторый код, использующий RTTI для обработки любого переданного объекта и проверяющий каждый клик для полноразмерного изображения. Значения свойств соответствуют атрибутам присоединенным к ним.

Уловили? Отлично, теперь давайте посмотрим.

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

Взглянем на исходный код на одного из классов:

MinimumIntegerAttribute = class(BaseIntegerValidationAttribute)

private

FMinimumValue: Integer;

public

constructor Create(MinimumValue : Integer; const FailureMessage : string);

function Validate(Value : Integer) : boolean; override;

end;

Конструктор содержит два параметра, метод проверки очень прост (в этом случае правило простое). Выглядит это так:

function MinimumIntegerAttribute.Validate(Value: Integer): boolean;

begin

Result := Value >= FMinimumValue;

end;

Теперь я определил атрибуты и могу использовать их так:

TPerson = class

private

FName: String;

FAge: Integer;

public

[NonEmptyString('Must provide a Name')]

property Name : String read FName write FName;

[MinimumInteger(18, 'Must be at least 18 years old')]

[MaximumInteger(65, 'Must be no older than 65 years')]

property Age : Integer read FAge write FAge;

end;

Да, еще один TPerson. Не очень оригинально.

Как вы видите, я снабдил свойство Name атрибутом NonEmtpyString, и добавил сообщение об ошибке для неверного значения. Я также отметил свойство Age атрибутами minimum и maximum. Вероятно, более полезным является определение атрибута для диапазона, но я хотел показать применение к одному свойству двух атрибутов.

Ради этого примера, я создал простую функцию, которая делает проверку:

function Validate(Target : TObject; ErrorList : TStrings) : boolean;

var

ctx : TRttiContext;

t : TRttiType;

p : TRttiProperty;

a : TCustomAttribute;

begin

Result := True;

if not Assigned(Target) then

raise Exception.Create('Can''t validate nil object');

if not Assigned(ErrorList) then

raise Exception.Create('Can''t validate with a nil ErrorList');

ctx := TRttiContext.Create;

try

t := ctx.GetType(Target.ClassType);

for p in t.GetProperties do

for a in p.GetAttributes do

if a is BaseIntegerValidationAttribute then

begin

if not BaseIntegerValidationAttribute(a).Validate(p.GetValue(Target).AsInteger) then

ErrorList.Add(BaseValidationAttribute(a).FailureMessage);

end

else if a is BaseStringValidationAttribute then

begin

if not BaseStringValidationAttribute(a).Validate(p.GetValue(Target).AsString) then

ErrorList.Add(BaseValidationAttribute(a).FailureMessage);

end

finally

ctx.Free;

end;

end;

Сначала я делаю проверку, чтобы убедиться, что преданный экземпляр объекта не nil, и то же самое для Errorlist, в который я буду заносить различные сообщения.

Далее, для каждого свойства, я выберу все атрибуты. В зависимости от типа атрибута, я вызову метода проверки, передавая значение для проверки. Если вызов Validate не удался, я добавляю FailureMessage в список ErrorList.

Безусловно, вы можете сделать лучше . Например, мне не нравится, что я должен обновлять метод Validate для каждого проверяемого типа и просто выводить ошибки в TStrings. Представлять список ValidationFailure объектов, вероятно, будет более правильно. Тем не менее, за 45 минут работы, я думаю это описывает идею достаточно хорошо. Вы можете скачать образец кода здесь.

Надеюсь это будет вам полезно, если вы захотите использовать атрибуты для персистентности, проверки данных или для чего-либо другого.
Несколько человек говорили, что они не видят смысла в атрибутах. Возможно, стоит изучить их и оставить на некоторое время, чтобы они успели "созреть" в вашей голове. Говорят, что наш язык формирует наши мысли, и это может быть просто потому, что Delphi в прошлом не поддерживал атрибуты, мы не привыкли к думать о них в поисках решения. Как и с дженериками, анонимными методами и интерфейсами до них, я нахожу, что чем больше я изучаю их, тем больше появляется идей их использования.

Original (English): More Attributes in Delphi 2010

Translation: © r3code, TDelphiBlog .

translated.by crowd

Like this translation? Share it or bookmark!