Reflection: Inspection of a Type s Metadata


Reflection: Inspection of a Type's Metadata

The act of inspecting a type's metadata at runtime is called reflection , and it is a fundamental facility of the CLR. In fact, reflection is so fundamental that the type system's base class, System.Object , has a method GetType specifically designed to facilitate this inspection. The GetType method returns an object of type Type ”a slightly unfortunate choice, as this overloading of the word type can sometimes prove confusing. To distinguish between the two usages, in this chapter the term type, with a lowercase t, normally means any type in the CLR, such as the Object or String type. The term Type , with a capital T, refers to the Type class provided by the Base Framework. The GetType() method returns a singleton Type object for the type on which it was invoked. The Type class provides a means for developers to access the metadata of the type it describes.

Reflection Classes

A number of classes are used in the .NET Framework's reflection facilities. This section describes the architecture of these classes and demonstrates how developers use these classes. Note that a full and complete description of all of the functionality of the reflection classes is beyond the scope of this book. Instead, we provide a generalized overview of the structure of these classes; users should then be able to extrapolate the general usage.

The MemberInfo and Type classes are the foundation for the reflection facilities. The Type class provides several methods and properties to permit inspection of a type's metadata. It inherits from the MemberInfo class, which in turn inherits from Object . Figure 3.1 shows the relationship between fundamental classes in the reflection hierarchy.

Figure 3.1. The reflection class hierarchy

graphics/03fig01.gif

MemberInfo Class

The MemberInfo class serves as a base class for many of the reflection " Info " classes ”that is, classes whose names end in the word " Info " and are extensively used in the CLR's reflection services. [1] The MemberInfo class is abstract. Subtypes of MemberInfo include the following:

[1] The names of some other classes in the Base Framework also end in "Info" ”for example, CultureInfo . The discussion here focuses specifically on those classes related to the reflection services.

  • EventInfo , a class whose objects represent events of a type. Example properties and methods of this type include Name , GetAddMethod , GetRaiseMethod , and GetRemoveMethod .

  • FieldInfo , a class whose objects represent fields of the type. Example properties and methods of this type include Name , FieldType , and GetValue .

  • MethodBase , a base class for representing methods on a class, such as constructors and methods. Example properties and methods include IsConstructor , GetParameters , and Invoke .

  • PropertyInfo , a class whose objects represent properties of the type. Example properties and methods of this type include Name , CanRead , CanWrite , and GetAccessors .

  • Type , a class whose objects represent types in the CLR. The Type class is discussed shortly.

MethodBase serves as the abstract base class for the following classes:

  • ConstructorInfo , a class whose objects represent constructors available on the type. A ConstructorInfo object exists for each constructor that a class has. Example members of this type include ConstructorName , which is always .ctor ; TypeConstructorName , which is always .cctor ; and Invoke .

  • MethodInfo , a class whose objects represent methods of the type. Example properties and methods of this type include IsAbstract , ReturnType , and GetBaseDefinition , which is used to access the overridden method definition from the class's base type.

One other class is also used by many of the reflection " Info " classes:

  • ParameterInfo a class whose objects represent parameters of a method of the type. This class inherits from Object ., Example properties and methods of this type include IsIn , IsOut , and IsDefined .

This common base, MemberInfo , provides a number of methods that can be invoked on all " Info " classes as well as an abstract base class that is used as the return type of many methods. An example of the useful functionality specified by this class is the GetCustomAttributes method of the MemberInfo class. All members of a type can be annotated with custom attributes, which consist of user -defined metadata annotations. (Custom attributes are explained in detail later in this chapter.) The GetCustomAttributes method ensures that developers can access the custom attributes on any subclass of MemberInfo regardless of the subtype with which a developer is dealing. (The list of custom attributes on a member may, of course, be empty.)

Instances of most classes derived from MemberInfo are provided as singletons. For example, when a Type object is required for a type, it will be created once and that same object will be reused whenever a Type object is required for that specific type. The same situation applies to many of the other reflection classes. Thus, if an EventInfo object is created for an event on a certain type, then that object is reused whenever information is needed for that event on the particular type.

Another example of the usefulness of the MemberInfo class as a base class involves the GetMembers method defined on the Type type. It returns an array of MemberInfo objects, so that the array can hold instances of any derived class. As a consequence, a single array can hold Info classes for properties, events, methods, and fields.

Type Class

Like MemberInfo , the Type class is an abstract class. Like the String class described in Chapter 2, it provides a number of methods ”far too many to cover individually here. To simplify the description of the functionality offered by the members of the Type class, the methods and properties can be divided into groups based on the services they provide. From a very general viewpoint, the methods could be categorized as follows :

  • Get methods: Methods that start with the word Get and return information about one aspect of the type

  • Is methods or properties: Methods or properties that return a Boolean value indicating whether the type satisfies a condition

  • Properties: Properties of the type, such as AssemblyQualifiedName

  • Static fields: Fields holding information required to process the type

Get Methods

A number of methods on the Type class are prefixed with the word Get . As this naming convention suggests, these methods return information about an aspect of the type. For example, the GetMethod method returns a MethodInfo object that describes a method defined by the type. GetMethod requires a parameter of type String that supplies the name of the method needed. By comparison, the GetMethods method does not require a string to designate which method is needed; it returns an array of MethodInfo objects that represents all of the publicly available methods for a type.

Is Methods and Properties

A number of properties on the Type class return a Boolean value that describes a single aspect of the type. For example, the IsAutoLay property returns true or false to indicate whether the execution engine automatically lays out the type at runtime. Sometimes symmetrically related properties are available. For example, the IsExplicitLayout , IsLayoutSequential , and IsAutoLayout properties indicate whether the developer has specified a specific memory layout for the class. One and only one of these properties can be true for any type.

Properties

Each type has a number of properties, one variety of which is the Is properties described previously. Other properties include the required properties AssemblyQualifiedName , BaseType , and FullName . Every type must have these properties, which have only a single value.

Static Fields

Static fields represent values that are shared by all types in the CLR. For example, the EmptyTypes field represents an empty array of Type objects.

For a complete list of the members of the Type class, consult the current definition of Type in the SDK. Table 3.1 briefly describes the functionality of the Type class's methods.

Example: Using Reflection

It is often much easier to understand how the classes function together by working through a simple example. The program fragment in Listing 3.1 builds on many of the concepts already discussed. The code demonstrates the use of the Type object for the Object class. This program accesses the Type object for the System.Object class. It then writes a number of the type's properties to the console as well as a string representing each member that the type has.

Listing 3.1 Use of the Type object for the Object class
 using System; using System.Reflection; namespace ReflectionSample {   class ObjectSample   {     static void Main(string[] args)     {       Type t = Type.GetType("System.Object");       Console.WriteLine(t.FullName);       Console.WriteLine(t.AssemblyQualifiedName);       Console.WriteLine("GUID: " + t.GUID);       Console.WriteLine("IsAbstract: " + t.IsAbstract);       Console.WriteLine("IsClass: " + t.IsClass);       Console.WriteLine("IsInterface: " + t.IsInterface);       Console.WriteLine("IsSealed: " + t.IsSealed);       MemberInfo[] members = t.GetMembers();       foreach(MemberInfo m in members)         Console.WriteLine("\t{0}",m);     }   } } 
Table 3.1. Type Class Methods

Method Name

Description

BaseType

Returns the base type's Type object; allows dynamic discovery of a type's inheritance hierarchy

FindInterfaces

Returns an array of Type objects representing the interface types supported by the type

FindMembers

Returns an array of MemberInfo objects representing the members of the type

AssemblyQualifiedName

Returns the type's fully qualified name

GetConstructors

Returns an array of ConstructorInfo objects representing the constructors of the type

GetMethods

Returns an array of MethodInfo objects representing the public methods of the type

GetNestedTypes

Returns an array of Type objects representing the nested types of the type

IsAbstract

Returns a Boolean value indicating whether the type is abstract

IsInterface

Returns a Boolean value indicating whether the type is an interface

InvokeMember

Invokes a member of the type

IsPublic

Returns a Boolean value indicating whether the type is publicly accessible

IsSealed

Returns a Boolean value indicating whether the type is sealed

IsValueType

Returns a Boolean value indicating whether the type is a value type

You are already familiar with the Object class. The code in Listing 3.1 uses the Type class's static GetType method to return a reference to the singleton Type object for the Object class. As the sample programs in this book are written in C#, you could use the C# keyword typeof to access the Type object for a type. The static method is used here because not all languages have a typeof keyword but all languages should be able to call the GetType method from the Framework Class Library.

Next, the program displays some properties, such as the type's full name, its assembly qualified name, [2] and its GUID. A GUID is stored with the class for interoperability with COM but is not a fundamental aspect of identifying a type in the CLR as it is in COM.

[2] You may be puzzled about exactly what an assembly qualified name is and why it is so detailed. This explanation will appear later in this chapter.

Next, the program displays some information about the type: Is it abstract? Is it a class? Is it an interface? Is it sealed?

Finally, the GetMembers method is called to retrieve an array of MemberInfo -derived classes. Each element in the array represents one member of the type. The program iterates through the array and prints out a string representation for each element.

Listing 3.1 produces the following output:

 System.Object  System.Object, mscorlib, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 GUID: 81c5fe01-027c-3e1c-98d5-da9c9862aa21 IsAbstract: False IsClass: True IsInterface: False IsSealed: False         Int32 GetHashCode()         Boolean Equals(System.Object)         System.String ToString()         Boolean Equals(System.Object, System.Object)         Boolean ReferenceEquals(System.Object,                                 System.Object)         System.Type GetType()         Void .ctor() 

Note that if you run the program fragment in Listing 3.1, you may get slightly different output. In particular, the version of the .NET Framework that you are using may differ from that shown here. Essentially, however, the output will provide the same information. Listing 3.1 clearly demonstrates how simple the reflection facilities are. To reinforce a recurring theme, we do not know in which language Object was written, but this fact is of no consequence. You could also write the sample program in any CLR language and retrieve and display the same information.

Listing 3.2 extends Listing 3.1 by displaying more metadata, but this time about the String class. This code reveals its constructors and the name of the parameters.

Listing 3.2 Use of the Type object for the String class
 using System; using System.Reflection; namespace StringSampleExtended {   class Sample   {     static void Main(string[] args)     {       Type t = Type.GetType("System.String");       Console.WriteLine(t.FullName);       Console.WriteLine(t.AssemblyQualifiedName);       Console.WriteLine("GUID: " + t.GUID);       Console.WriteLine("IsAbstract: " + t.IsAbstract);       Console.WriteLine("IsClass: " + t.IsClass);       Console.WriteLine("IsInterface: " + t.IsInterface);       Console.WriteLine("IsSealed: " + t.IsSealed);       ConstructorInfo[] constructorArray =                         t.GetConstructors();       Console.WriteLine("Constructors: " +                         constructorArray.Length);       foreach(ConstructorInfo c in constructorArray)       {         Console.WriteLine("\t{0}",  c);         ParameterInfo[] parameterArray = c.GetParameters();         foreach(ParameterInfo p in parameterArray )            Console.WriteLine("\t\t{0}", p.Name);       }     }   } } 

The basic structure of Listing 3.2 is the same as that for Listing 3.1, the example using the Object class. The first really different line of code is the one that uses the String 's Type object to retrieve an array of ConstructorInfo objects. The number of constructors, represented by the length of the array, is written to the console. Next, the program loops through the array of ConstructorInfo objects using the C# foreach looping construct. The first line inside the loop implicitly uses the ToString method on each ConstructorInfo object to output a string representing itself on the console. Then, for every constructor, the program retrieves an array of ParameterInfo objects. It uses the ParameterInfo object's Name property to display its name.

Listing 3.2 produces the following output:

 System.String  System.String, mscorlib, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 GUID: 296afbff-1b0b-3ff5-9d6c-4e7e599f8b57 IsAbstract: False IsClass: True IsInterface: False IsSealed: True Constructors: 8         Void .ctor(Char*)                 value         Void .ctor(Char*, Int32, Int32)                 value                 startIndex                 length         Void .ctor(SByte*)                 value         Void .ctor(SByte*, Int32, Int32)                 value                 startIndex                 length         Void .ctor(SByte*, Int32, Int32,                    System.Text.Encoding)                 value                 startIndex                 length                 enc         Void .ctor(Char[], Int32, Int32)                 value                 startIndex                 length         Void .ctor(Char[])                 value         Void .ctor(Char, Int32)                 c                 count 

Note that String resides in the same assembly as Object ”namely, mscorlib ”and that String is a sealed class ”that is, you cannot create subtypes of String . All instance constructors share the same name, .ctor . Although the output from Listing 3.2 does not include one, the single class constructor is called .cctor . A type can have only one constructor because the constructor for the class is automatically called at runtime and no provision has been made for developers to supply an argument list to that call. As a consequence, you cannot overload the class constructor signature.

Listings 3.1 and 3.2 demonstrate only a fraction of the functionality available with the reflection facilities for ascertaining metadata on a type.

Example: Use of Type as an Abstract Type

If, as mentioned previously, Type is an abstract type, then exactly what type of object is returned when you invoke the GetType method? The simple answer is found by invoking the GetType method on the Type object returned by GetType . Listing 3.3 does exactly that for the String 's Type object.

Listing 3.3 Invoking the GetType method on the Type object returned by GetType
 using System; namespace GetTypeGetType {   class Sample   {     static void Main(string[] args)     {       Type t = Type.GetType("System.String");       t = t.GetType();       Console.WriteLine(t.FullName);       Console.WriteLine(t.AssemblyQualifiedName);       Console.WriteLine("Base Type: " + t.BaseType);       Console.WriteLine("Attributes: " + t.Attributes);       Console.WriteLine("Module: " + t.Module);     }   } } 

Listing 3.3 produces the following output:

 System.RuntimeType  System.RuntimeType, mscorlib, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Base Type: System.Type Attributes: AutoLayout, AnsiClass, NotPublic, Sealed, Serializable, BeforeFieldInit Module: CommonLanguageRuntimeLibrary 

This output demonstrates some other aspects of the CLR, such as versioning and public keys, which are covered later in this book.



Programming in the .NET Environment
Programming in the .NET Environment
ISBN: 0201770180
EAN: 2147483647
Year: 2002
Pages: 146

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