I would like to query some attributes (aka annotations) in a class I'm writing.
I can do it the complex way, but I'd like to use Spring4D instead.
It has a unit called Reflection
that looks like it has what I need, but I don't know how to use it.
I've looked on the net, but there are no examples on how to use this unit.
I found:
THasAttributeFilter
Which looks like this:
THasAttributeFilter<T: TRttiObject> = class(TMemberSpecificationBase<T>)
private
fAttributeClass: TAttributeClass;
protected
function Accept(const member: T): Boolean; override;
public
constructor Create(attributeClass: TAttributeClass);
end;
Great, so I create a filter like so:
type
Capability = (CanDoA, CanDoB);
TCapabilities = set of Capability;
[Capabilities(CanDoA)]
TMyClass = class(TParent)
strict private
class var fCapabilities: TCapabilities;
....
public
property Capabilities: TCapabilities read GetCapabilities;
end;
I can create a HasAttributeFilter like so:
HasAttr:= THasAttributeFilter<TMyClass>.Create(CapabilitiesAttribute);
... ?? now what ??
How do I use Spring4D to test the attribute without having to write the zillions of lines that classical RTTI writing (as e.g. per Nick Hodges' book) requires?
These filters are implemented using the Specification Pattern. See also Spring.DesignPatterns.pas.
They are used when you need delegates like in the IEnumerable<T>.Where()
method.
Here is a code snippet from Spring4D itself (Spring.Container.Builder):
var
condition: TPredicate<TRttiMethod>;
method: TRttiMethod;
...
begin
condition := TMethodFilters.IsInstanceMethod
and TMethodFilters.HasAttribute(InjectAttribute)
and not TMethodFilters.HasParameterFlags([pfOut, pfVar])
and not TMethodFilters.IsConstructor;
for method in model.ComponentType.Methods.Where(condition) do
...
You can see that different specifications can be combined with boolean logic to form one expression: method has to be an instance method and has to have the InjectAttribute, must not have out or var paramaters and should not be a constructor.
This combined specification can then be assigned to a TPredicate<T>
which can then be passed to the Where method.
The TSpecification<T>
which is returned by each of the TMethodFilters is a record with operator overloading which makes the boolean logic and assigning it to a TPredicate<T>
possible.
Using the specification pattern leads to very readable and composable code because you keep everything separated. The looping does not get cluttered with the internals how to determine if its a constructor or what kinds of parameters the method has because every specification is encapsulated into its own class.
P.S. Methods
is a property from the TRttiTypeHelper
from Spring.Helpers which returns IEnumerable<TRttiMethod>
.