Article

This is in continuation to Reflection in .NET - Part 1

Retrieving a Type

System.Type class

Every CLR object or value is an instance of some type. A type itself is an instance of type System.Type. An instance of System.Type can represent any one of the following types: Class, Array, Interface, Enumeration, Value types. This instance encapsulates the information associated with the type being retrieved. It offers several properties and methods. Using them, you can obtain information about the type at runtime. The information can be about its fields, properties, events, constructors, methods, the implementing module, and the container assembly in which the type resides.

If two object references of type Type refer to the same object it means that they represent the same type. The vice-versa also holds true. In case, you wish to ascertain whether two expressions are of the same type, you can compare their Type objects.

If you have an object and you don't know what type it is, you can call the System.Object.GetType() method on it. Recall that the System.Object is a base class for all types and every .NET type inherits from this class. This method returns an instance of the System.Type class. The returned instance represents the type of object on which GetType() is invoked.

The System.Type is the basis for all reflection functions. It is the primary way to retrieve information about the assembly's metadata. Once you obtain the Type of an object, the reflection operations can then be used to access the metadata from the assembly.

As soon as you have the Type instance for a type, you can use these properties to find out whether a given type is a class, an array, an interface, public, sealed, etc: IsClass, IsInterface, IsValueType, IsArray, IsAbstract, etc.

System.Type provides methods such as: GetFields(), GetMethods(), GetConstructors(), GetProperties(), GetInterfaces(), GetEvents(). These are the commonly used methods. Each such method returns a corresponding array that represents the items you are interested in. For example, if you want to obtain a collection of the fields for a given type, you will call GetFields() and it will return an array of FieldInfo objects. Similarly, if you want to obtain a list of the constructors for a given type, you will call GetConstructors() and it will return an array of ConstructorInfo objects.

Now let's say if you are looking for a specific method instead of an array of all methods. Reflection API provides a singular form corresponding to each of these methods that allows you to obtain a specific item that matches the name you specify in the search criteria. Examples include GetMethod(), GetField().

Use the System.Type.GetMembers() method if you want to obtain a list of all the members of a given type. It returns an array of MemberInfo objects that will provide you with basic information about every member of the type.

.NET offers a Type.GetType() method. It is an overloaded public static method that returns an object of type Type.

//The name corresponds to the fully qualified name of the type whose Type object 
//you wish to obtain. The name of the type should be qualified with a namespace.
GetType(string name)

//throwException tells the runtime whether it should throw an exception if the name 
//is not found. If the specified type is not found and throwException is set to true, 
//the runtime throws an exception of type TypeLoadException
GetType(string name, bool throwException)

//The ignoreCase tells the runtime whether the name is case-sensitive
GetType(string name, bool throwException, bool ignoreCase)

You can use the Type.FindMembers() method to look for specific members of a type. The method returns a filtered array of MemberInfo objects of the specified member type. For instance, you will use this method if you want to search for public methods whose names begin with 'To'.

The method accepts the following:

a. The type of member you want to search. For example, if you want to search methods, you will specify MemberTypes.Method. If you want to search fields, you will specify MemberTypes.Field where MemberTypes is an enumeration.

b. A bitwise combination of one or more BindingFlags that indicates how the search is to be performed. BindingFlags is discussed later.

c. A delegate that determines if the member currently being examined matches the specified filter criterion. Its objective is to filter the list of members in the array of MemberInfo objects. For example, if you want to filter on name, you'll use the Type.FilterName filter.

d. A filter criterion that is used by the filter. For example, if you're looking for methods whose names begin with 'To', you'll specify To*

using System;
using System.Reflection;

class Book {
    int isbnCode;
    string name;
}

Book obj = new Book();

//The variable t1 refers to an instance of type System.Type that 
//represents the Book type
Type t1 = obj.GetType();

//The variable t2 refers to an instance of type System.Type that 
//represents the Int32 type
Type t2 = obj.isbnCode.GetType();

//The variable t3 refers to an instance of type System.Type that
//represents the String type
Type t3 = obj.name.GetType();

Book obj2 = new Book();
Type t4 = obj2.GetType();

//Both t1 and t4 refer to the same instance because they represent 
//the same type i.e. Book.

Type t5 = typeof(int);
Console.WriteLine(t5.Name);     //Int32

//Both t2 and t5 refer to the same instance since both represent the Int32 type.
Console.WriteLine(t2.Name);     //Int32

typeof operator

If you want to obtain the metadata for a given type and you do not have its object in hand, the System.Object.GetType() won't work in this case because it only operates on living type instances.

The typeof operator lets you obtain the Type object without having to create an instance of the type. It accepts a type as a parameter 'typename' and returns an instance of System.Type that represents the 'typename'.

Consider when you do not have an instance of a Student object in hand. If you want to obtain the System.Type instance that represents the Student type, you cannot use the System.Object.GetType() method because it can only be invoked on an object. In this situation, you can use typeof operator.

public class Student { ... }
...
Type t = typeof(Student);

Pass to Type.GetType() method the name of your desired type and it will return the associated Type object.

try {
    Type type = Type.GetType("System.Int32", true);

    //This will throw the System.TypeLoadException because the type Double is not 
    //qualified with a namespace and we are instructing the runtime to throw an 
    //exception by passing a value true as the second argument.
    Type type = Type.GetType ("Double", true);
}
catch(TypeLoadException e) {
    Console.WriteLine(e.Message);
}

Querying a Type

If you want to query whether a type is a class, an array, serializable, private, and so on, you can use the corresponding public properties on its associated Type object.

[Serializable]
public class Student { ... }
...
Type t = typeof(Student);

Console.WriteLine(t.IsClass);       //true
Console.WriteLine(t.IsAbstract);            //false
Console.WriteLine(t.IsSerializable);    //true
Console.WriteLine(t.IsSealed);      //false
Console.WriteLine(t.IsInterface);           //false
Console.WriteLine(t.IsPublic);      //true

Querying for Attributes

Consider a custom attribute named CommentAttribute that can be placed on all target elements.

using System;
using System.Reflection;

[AttributeUsage(AttributeTargets.All, AllowMultiple=true)]
public class CommentAttribute : System.Attribute {
    private string text;
    public CommentAttribute(string text) { this.text = text; }
    public string Text { get { return text; } }
}

public class Square {
    [Comment ("It represents the length of a side of a square")]
    public int side;

    [Comment ("Last updated on April 20")]
    public int CalculateArea() {
        return side*side;
    }
    //Place the Obsolete attribute on Draw() method
    [Obsolete ("You can no longer draw a square", true)]
    public void Draw() {
        //Code
    }
}

Now we will use reflection to query the members of the Square type about the attributes attached to it.

public class QueryingAttributesDemo1 {
    public static void Main() {

        //Obtain the Type object associated with the Square class
        Type square = typeof(Square);

        //Use GetMembers() to obtain an array of MemberInfo objects. Each MemberInfo 
        //object encapsulates the information about a member of the Square class.
        MemberInfo[] squareMembers = square.GetMembers();

        //Iterate the array and display the attributes associated with each of its members
        for(int i = 0; i < squareMembers.Length; i++) {

            //Now you have a MemberInfo object. Call GetCustomAttributes on this object  
            //to obtain all the attributes that the user has placed on the member  
            //reflected by this MemberInfo instance. The Boolean false indicates that 
            //.NET should not search this member's inheritance chain to find the
            //attributes. It returns an array of objects.
            Object[] attributes = squareMembers[i].GetCustomAttributes(false);

            if(attributes.Length > 0) {
                Console.WriteLine("\n Attributes for {0} {1} are: ", squareMembers[i], 
                squareMembers[i].MemberType);

                for(int n = 0; n < attributes.Length; n++)
                    Console.WriteLine("{0}\n", attributes[n]);
            }
        }
    }
}

Output

Attributes for Int32 side Field are: CommentAttribute
Attributes for Int32 CalculateArea() Method are:  CommentAttribute
Attributes for Void Draw() Method are: System.ObsoleteAttribute

Querying the methods for their attached attributes

Consider that you want to know about the attributes that have been placed on a method.

public class QueryingAttributesDemo2 {
    public static void Main() {

        //Obtain the Type object associated with the Square class
        Type square = typeof(Square);

        //Use GetMethods() to obtain an array of MethodInfo objects. Each MethodInfo 
        //object encapsulates the information about a method defined  in the Square class.
        MethodInfo[] squareMethods = square.GetMethods();

        //Iterate through the array and display the attributes associated with each method
        for(int i = 0; i < squareMethods.Length; i++) {
            //Use GetCustomAttributes() to obtain the attributes that the user has 
            //placed on a method reflected by this MethodInfo instance.
            Object[] attributes = squareMethods[i].GetCustomAttributes(false);

            if(attributes.Length > 0) {
                Console.WriteLine("\n Attributes for {0} Method are: ", 
                                    squareMethods[i]);
                for (int n = 0; n < attributes.Length; n++)
                    Console.WriteLine("{0}\n", attributes[n]);
            }
        }
    }
}

Output

Attributes for Int32 CalculateArea() Method are: CommentAttribute
Attributes for Void Draw() Method are: System.ObsoleteAttribute

For Part 3, click this link: Reflection in .NET - Part 3