RAD Studio (Common)
ContentsIndex
PreviousUpNext
Using .NET Custom Attributes

.NET framework assemblies are self-describing entities. They contain intermediate code that is compiled to native machine instructions when the assembly is loaded. More than that, assemblies contain a wealth of information about that code. The compiler emits this descriptive information, or metadata, into the assembly as it processes the source code. In other programming environments, there is no way to access metadata once your code is compiled; the information is lost during the compilation process. On the .NET platform, however, you have the ability to access metadata using runtime reflection services.  

The .NET framework gives you the ability to extend the metadata emitted by the compiler with your own descriptive attributes. These customized attributes are somewhat analogous to language keywords, and are stored with the other metadata in the assembly.

  • Declaring custom attributes
  • Using custom attributes
  • Custom attributes and interfaces

Creating a custom attribute is the same as declaring a class. The custom attribute class has a constructor, and properties to set and retrieve its state data. Custom attributes must inherit from TCustomAttribute. The following code declares a custom attribute with a constructor and two properties:

    type
       TCustomCodeAttribute = class(TCustomAttribute)
          private
              Fprop1 : integer;
              Fprop2 : integer;
              aVal   : integer;
              procedure Setprop1(p1 : integer);
              procedure Setprop2(p2 : integer);
          public
              constructor Create(const myVal : integer);
              property prop1 : integer read Fprop1 write Setprop1;
              property prop2 : integer read Fprop2 write Setprop2;
       end;

The implementation of the constructor might look like

  constructor TCustomCodeAttribute.Create(const myVal : integer);
  begin
      inherited Create;
      aVal := myVal;
  end;

Delphi for .NET supports the creation of custom attribute classes, as shown above, and all of the custom attributes provided by the .NET framework.

Custom attributes are placed directly before the source code symbol to which the attribute applies. Attributes can be placed before

  • variables and constants
  • procedures and functions
  • function results
  • procedure and function parameters
  • types
  • fields, properties, and methods
Note that Delphi for .NET supports the use of named properties in the initialization. These can be the names of properties, or of public fields of the custom attribute class. Named properties are listed after all of the parameters required by the constructor. For example

      [TCustomCodeAttribute(1024, prop1=512, prop2=128)]
      TMyClass = class(TObject)
      ...
      end;

applies the custom attribute declared above to the class TMyClass

The first parameter, 1024, is the value required by the constructor. The second two parameters are the properties defined in the custom attribute. 

When a custom attribute is placed before a list of multiple variable declarations, the attribute applies to all variables declared in that list. For example

var
 [TCustomAttribute(1024, prop1=512, prop2=128)]
 x, y, z: Integer;

would result in TCustomAttribute being applied to all three variables, X, Y, and Z

Custom attributes applied to types can be detected at runtime with the GetCustomAttributes method of the Type class. The following Delphi code demonstrates how to query for custom attributes at runtime.

var
   F: TMyClass;          // TMyClass declared above
   T: System.Type;
   A: array of TObject;  // Will hold custom attributes
   I: Integer;

begin
   F := TMyClass.Create;
   T := F.GetType;
   A := T.GetCustomAttributes(True);

   // Write the type name, and then loop over custom 
   // attributes returned from the call to 
   // System.Type.GetCustomAttributes.
   Writeln(T.FullName);
   for I := Low(A) to High(A) do
      Writeln(A[I].GetType.FullName);
end.

You can call unmanaged Win32 APIs (and other unmanaged code) by prefixing the function declaration with the DllImport custom attribute. This attribute resides in the System.Runtime.InteropServices namespace, as shown below:

  Program HellowWorld2;

      // Don't forget to include the InteropServices unit when using the DllImport attribute.
      uses System.Runtime.InteropServices;

      [DllImport('user32.dll')]
      function MessageBeep(uType : LongWord) : Boolean; external;

      begin
            MessageBeep(LongWord(-1));
      end.

Note the external keyword is still required, to replace the block in the function declaration. All other attributes, such as the calling convention, can be passed through the DllImport custom attribute.

Delphi syntax dictates that the GUID (if present) must immediately follow the declaration of an interface. Since the GUID syntax is similar to that of custom attributes, the compiler must be made to know the difference between a custom attribute - which applies to the next declaration - and a GUID specifier, which applies to the previous declaration. Without this special case, the compiler would try to apply an attribute to the first member of the interface. 

When the compiler sees an interface declaration, the next square bracket construct found is assumed to be that of a GUID specifier for the interface. The GUID must be in the traditional Delphi form:

['{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}']

Alternatively, you can use the Guid custom attribute of the .NET framework ( GuidAttribute). If you choose this method, then you should introduce the attribute before the interface, as with any other custom attribute. 

The effect in either case is the same: the GUID is emitted into the metadata for the interface type. Note that GUIDs are not required for interfaces in the .NET Framework. They are only used for COM interoperability.

Note: When importing COM interfaces with the ComImport custom attribute, you must declare the GuidAttribute instead of using the Delphi syntax.

Copyright(C) 2008 CodeGear(TM). All Rights Reserved.
What do you think about this topic? Send feedback!