RAD Studio (Common)
|
.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.
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
[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.
Copyright(C) 2008 CodeGear(TM). All Rights Reserved.
|
What do you think about this topic? Send feedback!
|