Chapter 23. Attributes and Reflection

After completing this chapter, you will be able to:

  • Describe what attributes are.

  • Use attributes to add metadata to managed types.

  • Create your own attribute types.

  • Access attribute metadata from code.

This chapter introduces metadata and attributes and shows you how to start defining and manipulating metadata for your own .NET types.

Metadata and attributes

The concept of metadata is central to the way the Microsoft .NET Framework works, so to be an effective .NET programmer, you need to know what it is and how to work with it. Metadata is data attached to .NET data types that carries information about those types (in a broader sense, it is data that describes data). A lot of metadata contains information that can’t be specified in the programming language, and it offers a useful—many people would say essential—way to provide all the extra information needed by the .NET runtime.

One of the major advantages of metadata is that it is stored along with the code, so extra data doesn’t need to be stored separately. Traditionally, in Windows all extra data has to be stored in the Windows registry. One of the main problems with this is ensuring that the data in the registry doesn’t become corrupt or out of step with the code.

Another major advantage of metadata is that it provides a way to add version information to the code so that you know which version of a component you’re using. This solves a lot of problems that have plagued programmers since the early days of Windows; it is a huge step forward.

The compiler always attaches metadata to the output code to describe it, and the Common Language Runtime (CLR) uses the metadata to control the loading and execution of the code. You can also attach metadata to code by using attributes, which are special syntax elements that can be attached to classes and class members. You’ll see how to use attributes later in this chapter.

You can see some of the metadata that the compiler attaches to your code if you use the IL disassembler tool (ILDASM), which is included with the .NET Framework SDK. (You can find this tool in the Program FilesMicrosoft SDKsWindowsv8.0ainNETFX4.0 Tools folder.)

Using ILDASM

The following example shows you how to use ILDASM to examine a simple application:

  1. Start Microsoft Visual Studio 2012 and create a new CLR Console Application project named Hello.

  2. Add a new managed class to the application.

    ref class Hello
    {
    public:
        static void SayHello()
        {
            Console::WriteLine("Hello, world");
        }
    };

    The class doesn’t really have to do anything particular; it is simply here so that you can disassemble it to look at the metadata.

  3. Build the application to generate the executable.

  4. Run ILDASM. To do so, on the Tools menu, click Visual Studio Command Prompt, and then type ildasm on the command line.

  5. On the ILDASM File menu, click Open, navigate to the Hello.exe executable, and then open it.

    A window opens that should look similar to the following:

    A screenshot of the ILDASM main window, showing the structure of a .NET binary file as a tree of entries.
  6. We’re interested in the managed type Hello, which is indicated by the blue component symbol. Click the plus sign (+) to expand the tree for Hello and display the details of the class, as depicted in the following screen shot:

    The same screenshot as in the last illustration, except this one shows the “Hello” entry in the tree expanded, displaying successive levels of detail. Several symbols appear, denoting different types of entities. The symbols for methods are pink squares, and static methods display an “S” in the square.

    The type has three entries: the details of the class, and the entries for two methods, which are the SayHello method you added and the default constructor provided by the compiler.

  7. Double-click the red triangle to bring up the class information.

    A window similar to the following appears:

A screenshot of the window that opens after double-clicking the red triangle in the tree. The window contains code that describes the item.

The definition of the managed class—which extends System::Object—is marked as private auto ansi. These keywords represent items of metadata that have been added by the compiler to describe the class. You can open the other methods in this class to see what metadata is attached to them.

You can inquire about attributes at run time by using reflection, which is a feature by which programmers can obtain information about the objects they are using, such as what class the objects belong to, what methods the objects support, and what metadata is attached to them. Using attributes in code is very powerful because it gives you a way to extend the programming language, introducing new properties for your classes that don’t exist in the base language.

Later in the chapter, you’ll see how to create custom attributes and how to use code to look at the attributes attached to classes.

Using predefined attributes

In this section, you’ll learn how to use the attributes that are predefined by the .NET Framework. You can use these attributes in two ways: by editing the AssemblyInfo.cpp file that comes as part of a C++/CLI project, and by attaching attributes to managed elements in your code.

The AssemblyInfo.cpp file

Every C++/CLI project includes an AssemblyInfo.cpp file that contains code affecting the attributes applied to the assembly. You can edit this file to customize the assembly attributes, which will be used to set the metadata in the assembly at build time. The following exercise shows you how to modify assembly attributes:

  1. Create a new CLR Console Application project named AssemblyAttributes.

  2. Open the AssemblyInfo.cpp file and examine its contents.

    Observe that the file contains a number of entries of the following form:

    [assembly:AssemblyTitleAttribute("AssemblyAttributes")];

    Many of these have empty strings as arguments.

  3. Find the version number attribute and edit it to produce a new version, such as in the following example:

    [assembly:AssemblyVersionAttribute("1.1.105.3")];

    This number would correspond to version 1.1, build 105, revision 3.

  4. Compile and build the application. If you now look at the assembly by using ILDASM, you can see the version in two places. First, it will show in the pane at the bottom of the ILDASM main window, as demonstrated here:

    A partial screenshot displaying the bottom pane of the ILDASM main window. The version information appears in the pane as “.ver 1:1:105:3”.

    You can also see it by double-clicking the MANIFEST entry in the main window and scrolling down to the bottom of the data window that opens. The line within the .assembly Assembly Attributes block that begins with .ver is the one that lists the version metadata:

    .ver 1:1:105:3

You can check this version number in applications that use this assembly, but explaining how to do this is beyond the scope of this book.

Using the predefined attribute classes

Although much of the metadata produced by the compiler is predefined and you can’t alter it, a number of optional standard attributes are provided by various .NET Framework namespaces. The following table lists just some of the more than 300 standard attributes that you might want to use in your own projects:

Class

Description

System::AttributeUsageAttribute

Specifies the usage of another attribute class

System::CLSCompliantAttribute

Indicates whether an application element is CLS-compliant

System::Diagnostics::ConditionalAttribute

Indicates that a method can be called if a preprocessor symbol is defined

System::Diagnostics::DebuggableAttribute

Modifies code generation for run-time JIT (Just-In-Time) debugging

System::Diagnostics::DebuggerHiddenAttribute

Applied to a method to indicate that breakpoints can’t be set in the code and that debuggers will not stop in the method

System::Diagnostics::DebuggerStepThroughAttribute

Applied to a method to indicate that the debugger will not stop in this method, although breakpoints can be set

System::FlagsAttribute

Indicates that an enumeration is to be used as a set of flags and can be represented by a bit field

System::NonSerializedAttribute

Indicates that a field of a serializable class should not be serialized

System::ObsoleteAttribute

Indicates application elements that are no longer in use

System::ParamArrayAttribute

Indicates that a method accepts a variable number of arguments

System::Runtime::InteropServices::MarshalAsAttribute

Indicates how to marshal data between managed and unmanaged code

System::Runtime::InteropServices::StructLayoutAttribute

Determines how a type is laid out in memory

System::SerializableAttribute

Indicates that a class can be serialized

The following exercise shows you how to use one of the standard attributes in code. You’ll use the ObsoleteAttribute class to mark a class method as obsolete and see how the compiler gives a warning when you use the obsolete method. This exercise will also show you how to build a C++/CLI Dynamic-Link Library (DLL) and use it in code. If you want to know more about DLLs, take a moment to read the following sidebar.

This exercise shows you how to use a standard attribute as well as how to create and use a DLL. Imagine that you have a class that didn’t use properties but, instead, had an old-fashioned explicit getter function. You decide to add properties and want to inform developers that they should not be using the old getter function.

  1. Create a new CLR Console application named UseAttributes.

    You’ll add code to this project later on in the exercise.

  2. You create DLLs by using a Class Library project. In Solution Explorer, right-click the solution name. On the shortcut menu that appears, point to Add, and then click New Project.

  3. When the Add New Project dialog box appears, select Class Library, call the project MyDll, and then click OK

    The MyDll.h file opens in the editor.

  4. Edit the class definition so that it looks like this:

    namespace MyDll {
    
        public ref class TestClass
        {
            int val;
        public:
            TestClass(int n) : val(n) { }
    
            int getVal() { return val; }
    
            property int Val {
                int get() { return val; }
            }
        };
    }

    You can see that in addition to the class having a getter function, it now also has a property that does the same job.

  5. You don’t want to remove the getter function because that might break existing client code, so you mark it as obsolete by using the Obsolete attribute:

    [Obsolete("Use the Val property instead", false)]
    int getVal() { return val; }

    The Obsolete attribute alerts the compiler that this function shouldn’t be used, and the message should be used to inform developers why and what to do instead. The second argument directs the compiler as to whether to treat use of this function as an error or to only issue a warning.

  6. Build the application to ensure that you have no coding errors.

  7. To use the DLL from the console application, you must add a reference to the console project. Open the project properties dialog box for the UseAttributes project by right-clicking the project name (not the solution name!) and then, on the shortcut menu, click Properties. In the dialog box, click Common Properties, and then, in the pane on the left, click Framework And References.

  8. Click the Add New Reference button to open the Add Reference dialog box. In the pane on the left, click the Solution entry. Doing so shows all the projects in the current solution. You should see MyDll listed in the center pane: select the check box adjacent to it, and then click OK twice to dismiss the dialog boxes. If you expand the External Dependencies entry under the UseAttributes project, you should see that it now displays an entry for MyDll.

  9. Open the UseAttributes.cpp source file and add a using directive for the MyDll namespace.

    using namespace MyDll;
  10. Edit the main function to create an object and call its obsolete get method, as shown here:

    int main(array<System::String ^> ^args)
    {
        TestClass ^tc = gcnew TestClass(4);
    
        int n = tc->getVal();
    
        return 0;
    }
  11. Build the application.

    You should see the following warning from the compiler:

    UseAttributes.cpp(12): warning C4947: 'MyDll::TestClass::getVal' : marked as obsolete
              Message: 'Use the Val property instead'

You could also try changing the second argument to the Obsolete attribute to true, rebuild the entire solution by selecting Rebuild Solution from the Build menu, and check that use of the obsolete function is now treated as an error.

Note

When you use the Build command, Visual Studio only recompiles those files that have changed since the last build. Using Rebuild causes Visual Studio to rebuild the entire project. This can be useful when you’ve made significant changes.

Defining your own attributes

As you’ll see in this section, you can easily define custom attributes and use them in your projects. Custom attributes are quite simple to write because an attribute’s parameters are simply represented by a class with properties and methods. For example, suppose you had the following attribute attached to a class designed to control the generation of logging information at run time:

[LogAttribute("myfile.log", type=LogAttribute::AllMethods)]
ref class MyClass...

The attribute has two parameters: one for the log file name, and a second that determines the level of logging. The attribute is represented by a class called LogAttribute whose members contain the file name and type information. Information about the attribute class is included with the metadata for MyClass, and a LogAttribute object can be queried at run time to retrieve its parameters. You’ll see how to query attributes in code in the final section of this chapter.

You can use any class to represent an attribute, but you will often use a class that derives from System::Attribute because that will give you a number of useful methods.

The AttributeUsage attribute, represented by the System::AttributeUsageAttribute class, is a meta-attribute: an attribute that is applied to attributes. You attach an AttributeUsage attribute to the class that implements an attribute to indicate where it can be used. Here’s an example:

[AttributeUsage(AttributeTargets::Method)]

This attribute would indicate that the attribute class can be used only on methods. The following table lists the members of the AttributeTargets enumeration that control where attributes are valid:

Member

This attribute can be applied to…

All

Any element

Assembly

An assembly

Class

A class

Constructor

A type constructor

Delegate

A delegate

Enum

An enumeration

Event

An event

Field

A field (for instance, Data member)

Interface

An interface

Method

A method

Module

A Portable Executable (PE)

Parameter

A parameter

Property

A property

ReturnValue

A return value

Struct

A structure; for example, a value type

If you want to specify more than one target, you can combine two or more members together with the bitwise OR operator (|), as you’ll see in the next exercise. As you might expect, an attribute without AttributeUsage can be applied to any code element.

Attribute class properties

Although some attributes have no parameters, most will specify at least one. Attribute parameters fall into two groups:

  • Positional parameters, which are identified simply by their position in the parameter list

  • Named parameters, which are specified as a name/value pair

Consider the custom attribute we used as an example:

[LogAttribute("myfile.log", type=LogAttribute::AllMethods)]

This attribute has one positional parameter and one named parameter called type. Positional parameters always appear before named parameters, are specified in a fixed order, and are passed to the class constructor. Named parameters are implemented as properties in the attribute class.

Design criteria for attribute classes

Before moving on to the exercise, here are a few design criteria that you should keep in mind when you write a custom attribute class:

  • Always add “Attribute” to the class name for an attribute (for example, call a class DocumentationAttribute rather than Documentation).

  • Use positional arguments for required parameters.

  • Use named arguments for optional parameters.

  • Provide a read-only property for each positional argument.

  • Provide a read/write property for each named argument. Be sure the name of the property differs in case from that of the argument (for example, for an argument called type, provide a property called Type).

Writing a custom attribute

This exercise shows you how to create a custom attribute that can be used to document methods and properties. In the next section, you’ll see how to write code that makes use of this attribute.

  1. Create a new CLR Class Library project named CustomAttributes.

    The custom attribute needs to be created as a DLL so that it can be used in other projects.

  2. Open the CustomAttributes.h header file and edit the skeleton class as follows:

    namespace CustomAttributes
    {
        [AttributeUsageAttribute(AttributeTargets::Method |
                       AttributeTargets::Property)]
        public ref class DocumentationAttribute : Attribute
        {
        };
    }

    Our class is called DocumentationAttribute and inherits from System::Attribute. The name follows the convention of having the class name for an attribute end with “Attribute.” The class is tagged with an AttributeUsage attribute that limits its use to class methods and properties. Note how you can use more than one member of the AttributeTargets enumeration by combining them with the bitwise OR operator.

  3. The attribute will include three pieces of data: the documentation text (which will be a positional parameter), and author and date strings (which will be optional—and thus implemented as named parameters). Add the declarations for the three members to the class.

    namespace CustomAttributes
    {
        [AttributeUsageAttribute(AttributeTargets::Method |
                       AttributeTargets::Property)]
        public ref class DocumentationAttribute : Attribute
        {
            String ^text;     // documentation text
            String ^author;   // optional author field
            String ^date;     // optional date field
        };
    }
  4. Add the constructor.

    public:
        DocumentationAttribute(String ^txt) : text(txt) { }

    The constructor takes a string as its only argument, saved away as the documentation text.

  5. Add a read-only property so that users can retrieve the text at run time.

    // Read-only property to return the text
    property String^ Text {
        String^ get() { return text; }
    }
  6. Add read/write properties to allow access to the two named parameters.

    // Properties for the positional parameters
    property String^ Author
    {
        String^ get() { return author; }
        void set(String ^au) { author = au; }
    }
    
    property String^ Date
    {
        String^ get() { return date; }
        void set(String ^dt) { date = dt; }
    }

    Choose the names for the properties carefully because these are going to be used in client code when using the attribute.

  7. Build the application to check that you haven’t made any errors.

  8. Add some code that will use the new attribute. In Solution Explorer, right-click the solution name. On the shortcut menu that appears, point to Add, and then select New Project. Ensure that the project type is set to CLR Console Application and call the project TestAtts.

  9. Add an external reference to the CustomAttributes DLL, just as you did in steps 7 and 8 of the previous exercise.

  10. Open the TestAtts.cpp file and add a using namespace line to the top of the file:

    using namespace CustomAttributes;
  11. Define a managed class that uses the new custom attribute, as demonstrated here:

    // A class to test the attribute
    ref class TestAtts
    {
        int val;
    public:
        [DocumentationAttribute(
          "The TestAtts class constructor takes an integer",
          Author="julian", Date="10/10/01")]
        TestAtts(int v)
        {
            val = v;
        }
        [DocumentationAttribute(
          "The read-only Value property returns the value of"
          " the int class member", Author="julian")]
        property int Value
        {
            int get() { return val; }
        }
    };

    The Documentation attribute has been attached to the two members of this class. The constructor uses all three possible parameters, whereas the property uses only the text and the Author named parameter.

    Note

    Remember that you can split a string literal over two lines, and as long as there is nothing between the closing and opening double quotation marks except white space characters, the preprocessor will concatenate them for you.

  12. Build the application to ensure that it compiles cleanly.

    You can now use ILDASM to see how the attribute data is held in the class.

  13. Run ILDASM, as described earlier, and open the TestAtts.exe file.

  14. Click the plus sign (+) next to the blue component symbol labeled TestAtts and then double-click the .ctor entry.

    This opens the disassembly for the constructor, as shown here:

    A screenshot of the IL disassembler tool window, displaying the code for the constructor, which shows the creation of a DocumentationAttribute object at the start of the function.

    You can see how the code creates a DocumentationAttribute object, which then forms part of the TestAtts object. You can access this attribute object from code. (You’ll see how to do this in the next section.)

  15. Before leaving this exercise, try adding the Documentation attribute to the class, like this:

    [DocumentationAttribute("The TestAtts class", Author="julian")]
    ref class TestAtts
    {
        ...
    }

    When you compile this code, the compiler will throw the following error message because the attribute cannot be applied to classes:

    TestAtts.cpp(8): error C3115: 'CustomAttribute::DocumentationAttribute': this attribute
    is not allowed on 'TestAtts'
              c:usersjuliandocumentssbscustomattributedebugcustomattribute.dll : see
    declaration of 'CustomAttribute::DocumentationAttribute'
              attribute can only be applied to: 'member function', 'property'

Using reflection to obtain attribute data

The final section of this chapter shows you how to use attributes at run time by inquiring about what attribute data an object contains.

The Type class

Before I talk about reflection and how it relates to attributes, you need to know something about the Type class. System::Type is a class that represents type declarations. This means that you can get a Type object to represent any object or type to which you have a reference, and you can then use that object to find out many details about the type. You can obtain Type objects to represent value types, arrays, classes, interfaces, and enumerations. It is the primary way to access metadata and the way in which you use reflection. Although the Type class is used mainly by developers writing language tools, you might find it useful at times, such as when you want to access class attributes.

System::Type has a lot of members (over 40 properties and almost 50 methods). The following two tables list a selection of properties and methods from this class to show you the sort of information you can access through a Type object:

Property

Description

Assembly

Gets a reference to the assembly where the type is defined

AssemblyQualifiedName

Gets the fully qualified name of the type, including the name of the assembly from which it was loaded

Attributes

Returns a TypeAttributes object representing the collection of attributes for this type

BaseType

Returns a Type for the type from which this object directly inherits

FullName

Returns the fully qualified name of the type, including namespace

IsAbstract

Returns true if the type is abstract

IsArray

Returns true if the type is an array

IsByRef

Returns true if the type is passed by reference

IsClass

Returns true if the type is a reference type (and not an interface or value type)

IsInterface

Returns true if the type is an interface

IsPublic, IsNotPublic

Indicates whether a type is marked as public or not

IsValueType

Returns true if the type is a value type

Module

Gets a reference to the module (the DLL) in which the type is defined

Namespace

Gets the namespace of the type as a string

UnderlyingSystemType

Gets a reference to the Type representing the CLR type underlying this language-specific type

Method

Description

GetConstructor, GetConstructors

Gets information about one or all of the constructors for the type

GetEvent, GetEvents

Gets information about one or all of the events defined for the type

GetField, GetFields

Gets information about one or all of the fields defined for the type

GetInterface, GetInterfaces

Gets information about one or all of the interfaces implemented by the type

GetInterfaceMap

Returns an InterfaceMapping showing how interface methods are mapped onto actual class methods

GetMember, GetMembers

Gets information about one or all of the members of the type

GetMethod, GetMethods

Gets information about one or all of the methods of the type

GetProperty, GetProperties

Gets information about one or all of the properties defined by the type

GetType

A static function that returns a Type object

InvokeMember

Invokes a member of the current type

ToString

Returns the name of the type as a String

You might think that you use the Attributes property to find out about custom attribute properties, but Attributes allows access only to standard system attribute data.

Accessing standard attributes

You can use the Type class’s Attributes property to find out about the standard attribute settings for classes. This property returns a TypeAttributes, which is a value type; it’s a set of flags describing which standard attributes are set for the type. This enumeration has over 30 members, and the table that follows shows you some of the common attributes that form part of TypeAttributes.

Member

Specifies that…

Abstract

The class is abstract

AnsiClass

Strings are interpreted using ANSI character encoding

AutoClass

The string encoding is automatically decided

Class

The type is a class

HasSecurity

The type has security information associated with it

Import

The type has been imported from another assembly

Interface

The type is an interface

NotPublic

The type is not public

Public

The type is public

Sealed

The type cannot be extended by inheritance

Serializable

The type can be serialized

UnicodeClass

Strings are interpreted by using Unicode character encoding

You can determine whether a type has an attribute set by using the bitwise AND operator (&), as shown in the following code fragment:

if ((tt->Attributes & TypeAttributes::Public) == TypeAttributes::Public)
    Console::WriteLine("Type is public");

If you want to check whether the type is a class, a value type, or an interface, you need to use the ClassSemanticsMask member, as illustrated here:

if ((tt->Attributes & TypeAttributes::ClassSemanticsMask) ==
        TypeAttributes::Class)
    Console::WriteLine("Type is a class");

Accessing custom attribute data

Custom attribute data is accessed by using the static GetCustomAttribute and GetCustomAttributes members of the Attribute class. As you’d expect, GetCustomAttribute retrieves information about one attribute, whereas GetCustomAttributes returns you an array containing details of all the custom attributes for a type. This exercise shows you how to use the Type class and the GetCustomAttributes method to retrieve the attribute settings from the class you created in the previous exercise.

  1. Continue with the project from the previous exercise.

  2. All classes dealing with reflection reside in the System::Reflection namespace, so add the following using declaration to the others at the top of the source:

    using namespace System::Reflection;
  3. You need to create a Type object to use reflection to find out about custom attributes, so add this code to the start of the main function:

    int main(array<String^>^ args)
    {
        Console::WriteLine("Testing Attributes");
    
        // Create an object and get its type
        TestAtts ^ta = gcnew TestAtts(3);
        Type ^tt = ta->GetType();
    
        return 0;
    }

    You obtain a Type object by using the GetType method that every .NET type inherits from System::Object.

  4. You can check whether there are any custom attributes on a class by using the GetCustom Attributes method on the Type object, like this:

    // See if there are any custom attributes on the class
    array<Object^> ^atts = tt->GetCustomAttributes(true);
    Console::WriteLine("Custom attributes on the class: {0}",
                              atts->Length);

    We know that the class doesn’t have any custom attributes, so you’d expect a count of 0. Note the second Boolean argument, which specifies that we want to include any attributes inherited from base classes.

  5. Build and run the application and check the output.

  6. To run the console application, you will need to set it as the startup project. In Solution Explorer, right-click the project name, and then, on the shortcut menu, click Set As Startup Project.

    The project name should now be displayed in bold. This will be the project that is started when you instruct Visual Studio to run the projects in the solution.

  7. The attributes are actually on the class members, not on the class itself, so get a list of the class members and query them, as shown in the following:

    // Get info on the class members
    array<MemberInfo^> ^mi = tt->GetMembers();
    Console::WriteLine("Class members: {0}", mi->Length);

    Calling GetMembers on the Type object returns an array of MemberInfo objects that describe the members. Running this code on the TestAtts class informs you that there are seven members.

    Note

    The seven members are the constructor, the private data value, the property get method, and four methods inherited from the Object base class (Equals, GetHashCode, GetType, and ToString).

  8. Loop over the list of class members and get the custom attributes for each one.

    for each (MemberInfo ^m in mi)
    {
        array<Object^> ^atts = m->GetCustomAttributes(true);
    
        if (atts->Length > 0)
        {
            Console::WriteLine("Attributes for member {0}:", m->Name);
            for each(Object ^att in atts)
            {
                Console::WriteLine(" attribute is {0}", att->ToString());
            }
        }
    }

    The outer loop considers each member in turn and calls GetCustomAttributes on the Member Info object to get a list of attribute objects. If there are any attribute objects for this member, we print them out.

  9. There are several ways to figure out whether a member has the Documentation custom attribute, and the following code shows one of them. Modify the code for the inner loop in the previous step so that it looks like this:

    for each (Object ^att in atts)
    {
        Console::WriteLine("  attribute is {0}", att->ToString());
        DocumentationAttribute ^da =
            dynamic_cast<DocumentationAttribute^>(att);
        if (da != nullptr)
        {
            Console::WriteLine("Doc attribute: {0}", da->Text);
        }
    }

    The loop first uses dynamic_cast to cast the current attribute as a DocumentationAttribute handle. If that returns a non-null value, you know that the cast worked, and so you can retrieve the Text.

  10. Build and run the application.

    You should see console output similar to that shown in the screen shot that follows, with a listing of the attributes present on class members and a showing of documentation text values.

    A screenshot of the console window with output showing that the constructor and the Value property have the Documentation attribute attached.

Quick reference

To

Do this

Modify the assembly-level attributes in a class.

Edit the entries in the AssemblyInfo.cpp file that is generated for all C++/CLI projects in Visual Studio 2012.

Find out about the standard attributes of a type.

Use the Attributes property on a Type object that represents the type, and use the bitwise AND operator (&) to compare the value with members of the TypeAttributes enumeration. For example:

if ((t->Attributes & TypeAttributes::Public) ==
    TypeAttributes::Public)

Create a custom attribute.

Create a class to represent an attribute, and use the AttributeUsage attribute to control where your attribute can be applied. For example:

[AttributeUsage(AttributeTargets::Method)] public
ref class MyAttribute { ... };

Represent mandatory parameters for a custom attribute.

Add arguments to the class constructor or constructors plus read-only properties to give access to the values.

Represent optional parameters for a custom attribute.

Add a property to represent each optional parameter.

Find out which custom attributes are attached to a class.

Create a Type object and use its GetCustomAttributes method to retrieve an array of objects representing the attributes attached to the class. For example:

Type ^tt = myObject->GetType();
array<Object^> ^atts =
    tt->GetCustomAttributes(true);

Find out which custom attributes are attached to a class member.

Create a Type object and use its GetMembers method to retrieve an array of MemberInfo objects representing the class members. Then call GetCustomAttributes on each MemberInfo object. For example:

Type ^tt = myObject->GetType();
array<MemberInfo^> ^mi = tt->GetMembers();
for each(MemberInfo ^m in mi) {
    array<Object^> ^atts =
        m->GetCustomAttributes(true);
    if (atts->Length > 0) {
        // Do something
    }
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.158.36