AddThis Social Bookmark Button

Print

Understanding Reflection, Part 1

by Nick Harrison
10/06/2003

Introduction

Reflection provides a way to examine and manipulate the runtime environment programmatically. This has several benefits, such as being able to discover types, methods, and properties at runtime; being able to access and manipulate attributes at runtime; and being able to invoke new methods at runtime. This article will focus on the first benefit; future articles will cover the others. The reflection concept is not unique to .NET -- Java, Eiffel, and SmallTalk all implement similar concepts to varying degrees. .NET benefits from learning from all of these earlier systems. Here we will focus on the .NET implementation.

The Great and Mighty Type Object

One common entry point for reflection is the Type object. Every object has a method, GetType, that will return the type associated with that object. C# also includes a typeof operator that will also return the Type object of a given class without having to have a variable of that type. The Type object itself also includes a GetType method that will return the Type object associated with a fully qualified type name. As you can see, it is fairly easy to get access to this object.

.NET Framework Essentials

Related Reading

.NET Framework Essentials
By Thuan L. Thai, Hoang Lam

Once you have the Type object, you can find out everything you want to know about the object you are dealing with.

Getting the Properties

The Type object includes a method, GetProperties, that will return an array of PropertyInfo objects. To iterate through this property collection, we can use code similar to this:


private  void GetProperties (Type theType)
{
  
  // Get the properties in the object
  PropertyInfo [] properties  = theType.GetProperties ();
  // loop through the properties
  foreach (PropertyInfo property in properties )
  {
    // Write out the name and data type for the property
    Response.Write (property.Name + " is of type " +
                    property.PropertyType.ToString() + "  and ");
    // Determine whether or not the property includes a get method
    if (property.CanRead == true)
      {
        Response.Write ("can be read" );
      }
    else
      {
        Response.Write ( "can not be read" );
      }
    Response.Write (" and ");
    // Determine whether or not the property includes a set method
    if (property.CanWrite == true)
      {
        Response.Write ("can be written to");
      }
    else
      {
        Response.Write ("cannot be written to");
      }
    Response.Write ("<br>");
    
  }        
}

Or, in VB.NET:


Public Shared Sub GetProperties(ByRef theType As Type)
  ' sample call GetProperties(GetType(System.Math))
  '
  ' Get the properties in the object
  Dim myProperties() As PropertyInfo = _
    theType.GetProperties((BindingFlags.Public Or BindingFlags.Instance))
  ' Loop thru the public properties
  Dim PropertyItem As PropertyInfo
  For Each PropertyItem In myProperties
    With PropertyItem
      Response.Write(.Name & " is of type " & .PropertyType.ToString & _
                    " And ")
      ' determine whether or not the property inclues a get
      If .CanRead Then
        Response.Write("can be read and ")
      Else
        Response.Write("cannot be read and ")
      End If
      ' determine whehter or not the property includes a set
      If .CanWrite Then
        Response.WriteLine("can be written.")
      Else
        Response.Write("cannot be written.")
      End If
    End With
    Response.Write ("<br>")
  Next
End Sub

Calling this function in a web page with a line similar to this:


private void Page_Load(object sender, System.EventArgs e)
{
  GetProperties (typeof (System.Web.UI.Page));
}

Running this example will result in a page that looks like the one in Figure 1:

Figure 1. Property Results

It is very easy to programmatically interrogate an object about its properties.

Getting the Methods

We can also easily get details about the methods included in a class using the GetMethods method, which will return an array of MethodInfo objects. To iterate through this method collection, we can use code similar to this:


private  void GetMethods (Type theType) 
{
  // Get the methods in the Object
  MethodInfo [] MethodInfoArray =
  theType.GetMethods(); 
  // Loop  through each of the methods 
  foreach (MethodInfo  method in MethodInfoArray ) 
  {
    // Write out the name and
    // return type for this method   
    Response.Write (method.Name + " returns " + method.ReturnType.ToString());
    // Determine whether or not a method is public or
    // private.   Note that private methods are
    // included in the array
    if (method.IsPrivate == true)
      {
        Response.Write (" but is private and cannot be executed "); 
      }
    // Determine whether or not the method is static
    // (shared in VB) or whether an object reference
    // wil be required 
    if (method.IsStatic == true)
      {
        Response.Write (" and does not require an object 
                        reference to be executed ");
      }
    // See if this method is a constructor
    if (method.IsConstructor == true)
      {
        Response.Write (" and is the constructor for " +  theType.Name);
      }
    Response.Write ("<br>");
  }
}

Or, in VB.NET:


Public Shared Sub GetMethods(ByRef theType As Type)
   ' sample call GetMethods(GetType(System.Math))
   '
   ' Get the methods in the object
   Dim myMethods() As MethodInfo = theType.GetMethods
   ' Loop thru the methods
   Dim MethodItem As MethodInfo
   For Each MethodItem In myMethods
       With MethodItem
           Response.Write(.Name & "returns " & .ReturnType.ToString)
           ' determine if method public or private -- note private methods 
           ' ARE in array
           If .IsPrivate Then
               Response.Write(" is private and can NOT be executed ")
           End If
           ' determine if shared (static)
           If .IsStatic Then
               Response.Write("and does NOT require instantiation ")
           End If
           If .IsConstructor Then
               Response.Write(" and is the contructor for " & theType.Name)
           End If
           Response.Write ("<br>")
       End With
   Next
End Sub

Calling this function from a web page with a line similar to this:


private void Page_Load(object sender, System.EventArgs e)
{
   GetMethods (typeof (System.Math));
}

Running this example will result in a page that looks like the one in Figure 2:

Figure 2. Method Results

Getting the Parameters

All of this information about the methods is great, but to be really useful, you also need to know the parameters for these methods. Fortunately, each MethodInfo object also includes a GetParameters method that will return an array of ParameterInfo objects. We can use these parameter info objects to find out what parameters a given method expects. To iterate through these parameter collections, we can use code similar to this:


private void GetParameters (Type theType)
{
  // Get an Array of  methods
  MethodInfo [] MethodInfoArray =  theType.GetMethods ();
  // Loop through the methods
  foreach (MethodInfo  method in MethodInfoArray )
  {
    Response.Write (method.Name +
                    " takes the following parameters: <ul>");
    // Get the parameters for each method
    ParameterInfo [] parameters = method.GetParameters ();
    // Point out if there are no parameters
    if (parameters.GetLength (0) == 0)
      {
        Response.Write ("<li>No Parameters");
      }
    // Output some details for each parameter
    foreach (ParameterInfo param in parameters )
      {
        Response.Write ("<li>"+param.Name + " of  type " +
                        param.ParameterType.ToString());
        
      }
    Response.Write ("</ul>");
  }
}

Or, in VB.NET:


Public Shared Sub GetParameters(ByRef theType As Type)
   ' sample call GetParameters(GetType(System.Math))
   '
   ' Get the list of methods
   Dim myMethods() As MethodInfo = theType.GetMethods
   ' Loop thru the methods
   Dim MethodItem As MethodInfo
   For Each MethodItem In myMethods
       Response.WriteLine()
       Response.WriteLine(MethodItem.Name & _
                          " takes the following parameters: ")
       ' Get the parameters for each method
       Dim myParms() As ParameterInfo = MethodItem.GetParameters
       ' point out if there are no parameters
       If myParms.Length = 0 Then
           Response.WriteLine("<li>No parameters.")
       End If 
       Dim ParmItem As ParameterInfo
       ' output some details for each parameter
       For Each ParmItem In myParms
           With ParmItem
               Response.Write("<li>" & .Name & " of type " & _
                              .ParameterType.ToString)
           End With
           Response.Write ("</ul>")
       Next
   Next
End Sub

Calling this function from a web page with a line similar to this:


private void Page_Load(object sender, System.EventArgs e)
{
   GetParameters (typeof (System.Math));
}

Running this example will result in a page that looks like the one in Figure 3:

Figure 3. Parameter Results

Finding All of the Types in the Same Namespace

One level higher in the reflection hierarchy than the Type is the Assembly, where the Type resides. The Type object provides ready access to the containing Assembly through the Assembly property. The Assembly object provides access to each of the Types contained in that Assembly through the GetTypes method, which will return an array of Types. To filter this list of Types to a single NameSpace, we will include a conditional statement to only show the ones where the NameSpace matches the namespace that we started with.


private  void GetTypes (Type theType )
{
  // Get the assembly for the type passed in
  Assembly theAssembly = theType.Assembly;
  // Find out which namespace this type is in
  string TargetNameSpace = theType.Namespace;
  Response.Write ("<h4>Types in the " + NameSpace + 
                  " namespace</h4>");
  // Get an array of the types in this assembly
  Type [] types = theAssembly.GetTypes ();
  // Loop through these types
  foreach (Type currentType in types)
  {
    // Display information only for the Types that are in the target assembly
    if (currentType.Namespace == TargetNameSpace )
    {
      Response.Write ( currentType.Name  + " is a ");
      if (currentType.IsPublic)
      {
        Response.Write (" public " );
      }
      else
      {
        Response.Write (" private ");
      }
    
      if (currentType.IsClass == true)
      {
        Response.Write ( "Class");
      }
      if (currentType.IsInterface == true)
      {
        Response.Write ( "Interface");
      }
      if (currentType.IsEnum == true)
      {
        Response.Write ( "Enumeration");
      }
      Response.Write ("<BR>");
    }
  }
}

Or, in VB.NET:


Public Shared Sub GetTypes(ByRef theType As Type)
  ' sample call GetTypes(GetType(System.Data.Column))
  '
  'Get the assembly for the Type passed in
  
  Dim TargetAssembly As [Assembly] = theType.Assembly
  ' Find out which namespace this Type is in
  Dim TargetNamespace As String = theType.Namespace
  Response.Write ("<h4>Types in the "  TargetNameSpace  _
                  " namespace</h4>")
  ' Get an array of the Types that are in this assembly
  Dim TargetTypes() As Type = TargetAssembly.GetTypes
  
  Dim TypeItem As Type
  ' Loop through these types
  For Each TypeItem In TargetTypes
    With TypeItem
      ' Display information only for the Types that 
      ' are in the target namespace
      If .Namespace = TargetNamespace Then
         Response.Write(.Name & " is a ")
         If .IsPublic Then
             Response.Write("public ")
         Else
             Response.Write("private ")
         End If
         If .IsClass Then
             Response.WriteLine("Class.")
         If .IsInterface Then
             Response.WriteLine("Interface.")
         If .IsEnum Then
             Response.WriteLine("Enumeration.")
         End If
         Response.Write ("<BR>");
      End If
    End With
  Next
End Sub

Calling this function from a web page with a line similar to this:


private void Page_Load(object sender, System.EventArgs e)
{
   GetTypes (typeof (System.Data.DataColumn));
}

Running this example will result in a page similar to Figure 4:

Figure 4. Types Results

Conclusion

Here we have seen how easy it is to query the runtime environment and get the details about the objects we are using. These techniques can be very useful, both in documenting the objects that we are using and in learning to use new classes. Next time, we will investigate how to use reflection with custom-defined attributes to extend the metadata available at runtime.

Nick Harrison UNIX-programmer-turned-.NET-advocate currently working in Charlotte, North Carolina using .NET to solve interesting problems in the mortgage industry.


Return to ONDotnet.com