
In my previous article
on Attributes in Delphi 2010 in brief , I showed the basics of creating, applying, and polling attributes. However, I did not give an example of what you could use them for.
Probably the most common example is for persistence, and, indeed, someone posted a similar example on the
Wings of Wind website. I would like to show their other uses - for data validation (Validation).
Note that what will be shown below is just an example of using attributes, and not a complete framework for checking data. Treat it as a test of concept, not as ready-made code.
')
So, with the warnings finished, in that case I would like to be able to add metadata to my classes specifying validation rules. Maybe I want the class
TPerson class to be considered correct when
Name is a non-empty value and
Age value ranges from 18 to 65. One way to achieve this is to provide the corresponding properties with attributes that define these rules, and then use some code that uses
RTTI to process any transmitted object and verifies each click for a full-sized image. Property values ​​correspond to attributes attached to them.

Got it? Great, now let's see.
I defined several attributes for data validation to show how they work with different types. A small hierarchy has been created that makes the verification code a bit more general. I created attributes for strings and numbers only, but you get the idea.
Take a look at the source code for one of the classes:
MinimumIntegerAttribute = class (BaseIntegerValidationAttribute)
private
FMinimumValue: Integer;
public
constructor Create(MinimumValue : Integer; const FailureMessage : string );
function Validate(Value : Integer) : boolean; override ;
end;
The constructor contains two parameters, the verification method is very simple (in this case, the rule is simple). It looks like this:
function MinimumIntegerAttribute.Validate(Value: Integer): boolean;
begin
Result := Value >= FMinimumValue;
end;
Now I have defined the attributes and can use them like this:
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;
Yes, another
TPerson . Not very original.
As you can see, I supplied the
Name property with the
NonEmtpyString attribute, and added an error message for the wrong value. I also noted the
Age property with
minimum and
maximum attributes. Probably more useful is defining an attribute for a range, but I wanted to show the application of two attributes to a single property.
For this example, I created a simple function that does a check:
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;
First, I do a check to make sure that the faithful instance of the object is not
nil , and the same for the
Errorlist , in which I will enter different messages.
Next, for each property, I will select all the attributes. Depending on the type of attribute, I will call the verification method, passing the value to be verified. If the call to
Validate fails, I add
FailureMessage to the
ErrorList list.
Of course, you can do better. For example, I do not like that I have to update the
Validate method for each type being checked and just output errors in
TStrings . Presenting a list of
ValidationFailure objects is likely to be more correct. However, in 45 minutes of work, I think this describes the idea quite well. You can download sample code
here .
I hope this will be useful for you if you want to use attributes for persistence, data validation or for something else.
Several people said that they did not see the point in attributes. It may be worth exploring them and leaving them for a while so that they can “mature” in your head. They say that our language shapes our thoughts, and this may be simply because Delphi did not support attributes in the past, we are not used to thinking about them in search of a solution. As with generics, anonymous methods and interfaces to them, I find that the more I study them, the more ideas there are to use them.
translated.by/you/more-attributes-in-delphi-2010/into-ru/transTranslation: © r3code, TDelphiBlog.