Attributes must descend from TCustomAttribute, if you look at the declaration of TCustomAttribute you will find that there is nothing special.

{ The base class for all custom attributes. Attributeinstances created by the RTTI unit are owned by thosemembers to which they apply. }TCustomAttribute = class(TObject)end;

Passing just the name of the new attribute is only practical in a few cases, usuallyyou need additional data associated. This is done through the constructor. The following example shows how to setup the call to the constructor in the attribute.

So its simple to declare an Attribute and decorate your types with them. Accessing the attributes stored in a given type involves using rtti.pas, I covered some of the basics of how this works in the previous post

Anything that can have attributes has an associated .GetAttributes() method that returnsthe array of the attributes associated with that code.

Attributes also have a few other special items that the compiler implements.

If you have an attribute named like this...

typeTestAttribute = class(TCustomAttribute)end;

It can be refered to in two different ways

[TestAttribute]TExample = class(Tobject)end;

[Test]TExample2 = class(TObject)end;

The compiler will look for the type, if it is not found it will automatically append "Attribute" to the name and search again. This is done to mimic the .NET behavior.

The compiler also has some special support for types allow you to get TRttiTypeeasily from the pTypeInfo pointer. The following code segment shows how that pTypeInfo can be interchanged for TRttitype in attributes.

12 comments:

Thank you for illustrating how "noisy" code quickly becomes with attributes. :)

This is another one of those language features that appeals to the laziness inherent in most developers.

We constantly seek to create code as quickly as possible, neglecting the fact that unless the code is "throw away", it will spend 99.9% of its life being maintained, not created.

Attributes make reading code physically harder by adding noise and conflating concerns (SQL attributes mixed in with test framework attributes, mixed in with xyz attributes and scattered through the declarations).

I'm not saying that attributes don't have some practical use, only that caution should be exercised in their use.

Where there is an alternative approach that keeps unrelated concerns separated and out of the declarations where they have no business being, that approach should be preferred, even if it takes a little longer to create that code.

You will reap the rewards in having something much more maintainable for the many months and years that follow those exciting few minutes of churning out some new code.

You can have additional functionality on your attributues. They are just classes.

One big thing I neglected is you should avoid doing any validation at the time of the construction, by raising an exception. Instead you should do it querying the object that uses the Attributes.

This way you have context and a class stack that makes sense. The Attribute Contruction will show you in deep in the rtti.pas and be confusing to debug. There are also other reasons, you can see them by reading the comments in RTTI.pas.

.NET attributes allow you to set property values on the attribute, as well as pass constructor parameters. You didn't specifically show an example, but I assume the Delphi/Win32 attributes can do this too?

Me understanding the attribute-concept halts on one reason. I still haven't found a place where it is useful to me. I don't want to stress you but you did mentioned coming articles of where custom attributes are practical.P.S. Great articles about the RTTI - a much overlooked topic.

Very late, but another use for attributes which we are finding very compelling at present is in unifying documentation, source and test for concurrency and threading: http://marc.durdin.net/2014/05/using-delphi-attributes-to-unify-source-test-and-documentation/