Using Reflection to Get 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. Querying attribute data is only one aspect of reflection, a feature of the .NET Framework 1.1 that lets you find out a lot of details about objects and the classes they belong to at run time. Not every aspect of reflection will be covered this chapter, but you’ll learn enough to be able to query attribute data.

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 you can get a Type object to represent any object 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 it was loaded from

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

GUID

Returns the GUID associated with the type, if any

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)

IsCOMObject

Returns true if the type is a COM object

IsInterface

Returns true if the type is an interface

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

GetTypeFromCLSID, GetTypeFromProgID

Static functions that get a Type object representing a COM object

InvokeMember

Invokes a member of the current type

ToString

Returns the name of the type as a String

You might think you need to 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 TypeAttributes, which is a value type; it’s a set of flags describing which standard attributes are set for the type. This enumeration has nearly 30 members, and the following table 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 using Unicode character encoding

You can find out 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) Console::WriteLine("Type is public");

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

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

Accessing Custom Attribute Data

Custom attribute data is accessed using the static GetCustomAttribute and GetCustomAttributes members of the Attribute class. As you’d expect, the GetCustomAttribute retrieves information about one attribute, whereas GetCustomAttributes returns you an array containing details of all the custom attributes for a type. This exercise will show 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 using the TestAtts project that you started in the previous exercise. All classes dealing with reflection live in the System::Reflection namespace, so add the following using declaration to the others at the top of the source:

    using namespace System::Reflection;
  2. 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 _tmain function:

    int _tmain() { Console::WriteLine(S"Testing Attributes"); // Create an object and get its type TestAtts* ta = new TestAtts(3); Type* tt = ta->GetType(); return 0; }

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

  3. You can see whether there are any custom attributes on a class by using the GetCustomAttributes method on the Type object, like this:

    // See if there are any custom attributes on the class Object* patts[] = tt->GetCustomAttributes(true); int n = patts->Length; Console::WriteLine( S"Number of custom attributes on the class is {0}", __box(n));

    We know that the class doesn’t have any custom attributes, so you’d expect a count of 0.

  4. The attributes are actually on the class members, not on the class itself, so get a list of the class members and query them:

    // Get info on the class members MemberInfo* pmi[] = tt->GetMembers(); int nMembers = pmi->Count; Console::WriteLine(S"Number of class members is {0}", __box(nMembers));

    Calling GetMembers on the Type object returns an array of MemberInfo objects that describe the members. Running this code on the TestAtts class tells 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).

  5. Loop over the list of class members, and get the custom attributes for each one:

    for (int i=0; i<pmi->Count; i++) { Object* pMemberAtts[] = pmi[i]->GetCustomAttributes(true); if (pMemberAtts->Count > 0) { Console::WriteLine(S"Attributes for member {0}:", pmi[i]); for(int j=0; j<pMemberAtts->Count; j++) { Console::WriteLine(S" attribute is {0}", pMemberAtts[j]); } } }

    The outer loop considers each member in turn and calls GetCustomAttributes on the MemberInfo object to get a list of attribute objects. If there are any attribute objects for this member, we print them out. As you’d expect, just passing an array element to WriteLine results in calling the appropriate ToString method, which normally prints out the name of the class.

  6. 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 Step 4 so that it looks like this:

    for (int j = 0; j < pMemberAtts->Count; j++) { Console::WriteLine(S"  att is {0}", pMemberAtts[j]); DocumentationAttribute* pda = dynamic_cast<DocumentationAttribute*>(pMemberAtts[j]); if (pMemberAtts[j]->GetType()->Equals(pda->GetType())) { Console::WriteLine( S"The member has the Documentation attribute"); Console::WriteLine(S"Text is ’{0}’", pda->Text); } } 

    The loop first uses dynamic_cast to cast the current attribute as a DocumentationAttribute pointer. Then the Equals method compares the Type object of the current attribute against that of the DocumentationAttribute class to see whether they are the same. If they are, the loop retrieves the Text member of the current attribute and prints it.

  7. Build and run the program. You should see console output similar to that shown here, with a listing of the attributes present on class members and a showing of documentation text values:

    click to expand




Microsoft Visual C++  .NET(c) Step by Step
Microsoft Visual C++ .NET(c) Step by Step
ISBN: 735615675
EAN: N/A
Year: 2003
Pages: 208

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net