--- title: X++ attribute classes description: Learn about the X++ attribute classes, including overviews and examples on creating an attribute class and metadata reflections. author: pvillads ms.author: pvillads ms.topic: conceptual ms.custom: - bap-template ms.date: 06/13/2024 ms.reviewer: johnmichalak audience: Developer ms.search.region: Global ms.search.validFrom: 2016-02-28 ms.dyn365.ops.version: AX 7.0.0 --- # X++ attribute classes [!include [banner](../includes/banner.md)] This article describes the use of attributes in X++. An attribute is a non-abstract class that extends (inherits from) the **SysAttribute** class. Attributes represent or store metadata about types and methods. An attribute can be attached to a class, a class field, a class method, an interface, or a table. Attributes are applied to the handlers of delegates and methods, to map the handlers to those targets. ## Creating an attribute class An attribute class can extend the **SysAttribute** class directly, or it can extend any descendant of the **SysAttribute** class. The **SysAttribute** class cannot be used as an attribute because it is declared **abstract**. The following example shows the declaration and design of an ordinary attribute class that you could create. ```xpp public class PracticeAttribute extends SysAttribute { // Fields in the classDeclaration. StartEnd startEndEnum; str reason; // Constructor. public void new(StartEnd _startEndEnum, str _reason) { startEndEnum = _startEndEnum; reason = _reason; } // Other methods can go here. } ``` ### Decorating a class with an attribute The following example shows a class and a method that are decorated with the **PracticeAttribute** given in the previous example. If the constructor of the attribute takes no parameters, the parentheses for the parameters are optional. The attribute decoration could be `[AnotherAttribute]` without parentheses. ```xpp [PracticeAttribute(StartEnd::End, "Use the RegularClass class at the end.")] public class RegularClass { [PracticeAttribute(Startend::Start, "Use the rehearse method at the start.")] public int rehearse() { // Logic goes here. } // More fields and methods belong here. } ``` You can omit the suffix of the attribute name if the suffix is `Attribute`. For example, you could use `[Practice]` instead `[PracticeAttribute]` in the preceding example. ### Attribute constructors You can enable your attribute class to store tailored metadata each time it is used to decorate a class, by having its constructor take parameters. The parameters for the constructor must be literals of the primitive types, such as **int,** **enum,** or **str**. The compiler does not construct an instance of the attribute class. It stores the name of the attribute class, plus the literal values for its constructor. Therefore, if the logic in an attribute constructor would throw an exception, the exception would not be found by decorating a class with the attribute. The exception would be found later when a process looks at a class to see the attribute it is decorated with. That is when the attribute is constructed. ### Naming conventions All attribute classes have the suffix **Attribute** in their name. The **Attribute** suffix is the name convention that we recommend, but it is not a system requirement. You can determine whether a class **extends** directly from **SysAttribute** by selecting the class in the **Application Explorer** and reviewing the **Extends** property in the **Properties** window. ## SysObsoleteAttribute The system provides several attributes, including the **SysObsoleteAttribute** class. One use of the **SysObsoleteAttribute** class is to notify the compiler that the compile should fail if a particular method is called in the source code. The compiler rejects the compile, and displays the specific message that is stored in this use of the attribute. The **SysObsoleteAttribute** class can also be used to notify the compiler to issue warning messages instead of errors. ### SysObsoleteAttribute code example ```xpp [SysObsoleteAttribute("The Automobile class might have faster performance.", false)] class Bicycle { // Members of the Bicycle class go here. } ``` ## Metadata reflection You use reflection to find the attribute metadata that is attached to a class. The classes to use for attribute reflection are as follows: - **DictClass** class – For classes and interfaces. - **DictMethod** class – For methods on classes, interfaces, or tables. On the previous reflection classes, the methods for reflecting on attribute metadata are as follows: - **getAllAttributes** method - **getAttribute** method - **getAttributedClasses** method - **getAttributes** method > [!NOTE] > There is no mechanism for listing all methods or classes that are adorned with a particular attribute from X++ code. However, because the X++ compiler records this information in the cross reference database, the information can be mined from there. ### Metadata reflection code example You use the **DictMethod** class to find the metadata value of an attribute that is decoration on a method. The following code example uses the **SysEntryPointAttribute** class as the attribute. It accepts your parameter values for the method name, and for the name of the class that contains the method. The **parmChecked** method is particular to the **SysEntryPointAttribute** class, and it is not inherited from its base class **SysAttribute**. Each attribute class can have its own method name for its metadata. ```xpp static public int MetadataOfSysEntryPointAttributeOnMethod ( str _sNameOfClass, str _sNameOfMethod ) { // Return Values: // 0 == Has the attribute, its metadata value is false; // 1 == Has the attribute, its metadata value is true; // 2 == The method lacks the SysEntryPointAttribute. int nReturnValue = -1, nClassId; boolean boolParmChecked; DictMethod dm; Object attributeAsObject; SysEntryPointAttribute sepAttribute; Global::info("Starting AttributeReflection" + " ::MetadataOfSysEntryPointAttributeOnMethod ...."); Global::info(strFmt ("Parameters are: _sNameOfClass = %1 , _sNameOfMethod = %2 .", _sNameOfClass, _sNameOfMethod) ); nClassId = Global::className2Id(_sNameOfClass); dm = new DictMethod (UtilElementType::ClassInstanceMethod, nClassId, _sNameOfMethod ); attributeAsObject = dm.getAttribute("SysEntryPointAttribute"); if (attributeAsObject is SysEntryPointAttribute) { sepAttribute = attributeAsObject as SysEntryPointAttribute; boolParmChecked = sepAttribute.parmChecked(); if (boolParmChecked) nReturnValue = 1; else nReturnValue = 0; Global::info( strFmt("Return value is %1.", nReturnValue) ); } else { nReturnValue = 2; Global::error("Object is not a SysEntryPointAttribute??"); } return nReturnValue; } /*** Output displayed in the Infolog. Message (05:03:22 pm) Starting AttributeReflection ::MetadataOfSysEntryPointAttributeOnMethod .... Parameters are: _sNameOfClass = CustCustomerService , _sNameOfMethod = create . Return value is 1. ***/ /************** // Simple AOT > Jobs job to run the method. static void AttributeReflection33Job(Args _args) { AttributeReflection::MetadataOfSysEntryPointAttributeOnMethod ("CustCustomerService", "create"); } **************/ ``` [!INCLUDE[footer-include](../../../includes/footer-banner.md)]