3

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?

Johan
  • 74,508
  • 24
  • 191
  • 319
  • You've got some typos in the property line that the editor won't let us change because they're apparently too trivial. You might want to edit them. – David Schwartz Jul 16 '14 at 15:57
  • @DavidSchwartz Attributes can be fed with everything that can be evaluated at compile time, not just "Integers and numbers". Furthermore, Embarcadero themselves don't prefix attributes with `T`. Nobody says they "must" start with a `T` – Günther the Beautiful Jul 16 '14 at 16:00
  • @DavidSchwartz, fixed the typo. You need to brush up on Attributes though, have a look at: http://www.malcolmgroves.com/blog/?p=476 – Johan Jul 16 '14 at 16:11

1 Answers1

3

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 snipped 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>.

Stefan Glienke
  • 20,860
  • 2
  • 48
  • 102
  • Thanks Stefan, The TPredicate usage does look really cool. I assume it works because of the parameter capturing that's happening in the anonymous methods? I.e. this will not work in D7, but will in XE. – Johan Jul 16 '14 at 19:18
  • Yes, in all versions that are supported by Spring4D (2010 and up). – Stefan Glienke Jul 16 '14 at 19:30