delphiattributesspring4d

How do I use THasAttributeFilter in Spring4d


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?


Solution

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