Implementing Self-Reflection with Extension Methods

Introduction

One of the books that was instrumental for me in writing bug free code was Dave Thielen's No Bugs! Dave Thielen was a key factor in writing the well-received MSDOS 5.0. (It was a long time ago.) After buggy MSDOS 4.0 everyone was ready for a less buggy version of DOS. Dave's No Bugs! Talked about tracing, trapping, and asserting long before those capabilities were built into the framework, long before there really was a framework--least a great one.

Now assertions and tracing are part of the .NET framework and have been all along. The ability to closely examine an object's state has been around for a while now too. Dumping your object's state was critical in checking for null pointers, especially in C++ programming. With the System.Reflection namespace and extension methods you can write a dozen or so lines of code that will use reflection to display any object or collection of object's state information. This article demonstrates how and along the way you will see a little LINQ and the DirectCast method.

Implementing Extension Methods in VB.NET

In VB.NET the easiest way to implement an extension method is to add a module to your project, declare one or more public sub routines or functions and add the Extension attribute. Here is part of the solution that demonstrates the basic syntax:

Public Module SelfReflect

  <Extension()>
  Public Sub Reflect(ByVal O As Object)
    Reflect(O, Console.Out)
  End Sub
End Module

A module is basically the same as C# progammings's static class. The ExtensionAttribute tells the compiler that the method is an extension method. That is, it will be used with member calling syntax object.method(). The first argument defines the type being extended. Again, in this example, it means that for all intents and purposes Reflect can be treated as a member method of the Object type. Since Object is the base class for all .NET classes they are treated as a member of every class even those you define.

Overloading Extension Methods

Like all methods extension methods can be overloaded. Overloading means to define a method with the same name as another method with a different parameter signature. Overloading solved the problem of needing to define and remember different method names based on parameter types, such as PrintInt, PrintString, PrintObject, etc. Having to define potentially dozens of differently named methods based on parameter type would make using a framework much more challenging. Overloading lets the compiler call the right method by examining the parameter signature. (Overloaded methods are actually uniquely named internally based on a concept called name mangling.)

To continue our reflection solution we now can add a second Reflect method that extends Object and accepts a TextWriter second parameter. Listing 1 shows the SelfReflect module with the overloaded Reflect method.

Imports System.Runtime.CompilerServices
Imports System.IO
Imports System.Reflection
Imports System.Collections


Public Module SelfReflect


  <Extension()>
  Public Sub Reflect(ByVal O As Object)
    Reflect(O, Console.Out)
  End Sub

  <Extension()>
  Public Sub Reflect(ByVal O As Object, ByVal writer As TextWriter)

    If (TypeOf O Is IEnumerable) Then DirectCast(O, IEnumerable).Reflect(writer)

    Dim info As PropertyInfo() = O.GetType().GetProperties()

    For Each item As PropertyInfo In info
      Try
        writer.WriteLine("{0} : {1}", item.Name, item.GetValue(O, Nothing))
      Catch
        writer.WriteLine("{0} : {1}", item.Name, item.ToString())
      End Try
    Next

    writer.WriteLine(O)
  End Sub

End Module


Listing 1: SelfReflect with two Reflect methods.

The second Reflect extension method accepts a TextWriter (which is what Console.Out is an instance of). The second overloaded method checks to see if the object argument is IEnumerable; if it is a third version of Reflect that is called by using DirectCast to cast the Object parameter to IEnumerable. If not System.Reflection is used to get the property information and dump the properties to the TextWriter object. Listing 2 contains the complete implementation of the SelfReflect class.

Imports System.Runtime.CompilerServices
Imports System.IO
Imports System.Reflection
Imports System.Collections


Public Module SelfReflect


  <EXTENSION()>
  Public Sub Reflect(ByVal O As Object)
    Reflect(O, Console.Out)
  End Sub

  <EXTENSION()>
  Public Sub Reflect(ByVal O As Object, ByVal writer As TextWriter)

    If (TypeOf O Is IEnumerable) Then DirectCast(O, IEnumerable).Reflect(writer)

    Dim info As PropertyInfo() = O.GetType().GetProperties()

    For Each item As PropertyInfo In info
      Try
        writer.WriteLine("{0} : {1}", item.Name, item.GetValue(O, Nothing))
      Catch
        writer.WriteLine("{0} : {1}", item.Name, item.ToString())
      End Try
    Next

    writer.WriteLine(O)
  End Sub

  <EXTENSION()>
  Public Sub Reflect(ByVal O As IList, ByVal writer As TextWriter)

    For Each elem In O
      Reflect(elem, writer)
    Next

  End Sub

  <EXTENSION()>
  Public Sub Reflect(ByVal O As IEnumerable, ByVal writer As TextWriter)

    For Each elem In O

      Reflect(elem, writer)
    Next

  End Sub

End Module


Listing 2: The complete implementation of the SelfReflect class.

The final two overloaded versions of Reflect are extension methods that iterate over the items in an IList or IEnumerable type. To test SelfReflect create some various kinds of objects and use the member of operator (,) to invoke Reflect. Listing 3 contains a Main console method that demonstrates.

Imports System.Runtime.CompilerServices
Imports System.IO
Imports System.Reflection
Imports System.Collections


Module Module1

    Sub Main()

      Dim str As String = "Hello World!"
      str.Reflect()

      Dim strings = {"Hello", "World!"}

      strings.Reflect()

      Dim chars = From ch In str.Split()
                  Select ch

      chars.Reflect()

      Console.ReadLine()

    End Sub

End Module


Listing 3: Testing SelfReflect

Main tests Reflect on a String, an array of strings, and the results from a LINQ query. All of the properties of each type, or each element of each type are properly displayed. (Since IList is IEnumerable you can leave that version of the Reflect method off.)

Summary

Adding code to help you debug has a special name now; it is referred to as instrumentation. The Reflect method and capability is part of that notion. By writing code that lets you immediately access an object's current state you can easily see if a particular object is not in an expected state. Combine this with Assert and Trace and you have taken several long strides forward in writing bug free code.

In this article you got to see the less cumbersome DirectCast method, how to implement extension methods, how to overload methods, and how to use Reflection. These are all valuable skills in the .NET developer lexicon.



About the Author

Paul Kimmel

Paul Kimmel is the VB Today columnist for CodeGuru and has written several books on object-oriented programming and .NET. Check out his upcoming book Professional DevExpress ASP.NET Controls (from Wiley) now available on Amazon.com and fine bookstores everywhere. Look for his upcoming book Teach Yourself the ADO.NET Entity Framework in 24 Hours (from Sams). You may contact him for technology questions at pkimmel@softconcepts .com. Paul Kimmel is a Technical Evangelist for Developer Express, Inc, and you can ask him about Developer Express at paulk@devexpress.com and read his DX blog at http:// community.devexpress.com/blogs/paulk.

Comments

  • Interesting

    Posted by kevininstructor on 04/21/2010 09:58am

    You can not create an extension method under VS2008 as you have done with option explicit, option strict and option infer = on. Actually I am surprised that you got it to work as I tried this prior to reading the VB.NET team blog where they said that extending objects was considered but later decided not too as it would be abused. Any ways I agree with Microsoft that creating extension methods for objects is more practice.
    
    Some good/simple ideas that for the most part give us method based rather than function base
         _
        Public Function ToInteger(ByVal sender As String) As Integer
            Return CInt(sender)
        End Function
    
    
         _
        Function CurrentRowCellValue(ByVal GridView As DataGridView, ByVal ColumnName As String) As String
            Return GridView.Rows(GridView.CurrentRow.Index).Cells(ColumnName).Value.ToString
        End Function
    
         _
        Function SingleQuotes(ByVal sender As String) As String
            Return String.Format("'{0}'", sender)
        End Function

    • you CAN create extension methods in VS 2008

      Posted by DEA_I on 04/27/2010 03:25am

      Kevin, I already used extension methods with VS 2008 and all the options on. What was your problem? Instead of your first extension take a look at Int32.Parse or even Int32.TryParse. I like the thing with the quotes - nice idea! Darius

      Reply
    Reply
  • Yes, in VS2008

    Posted by pkimmel on 04/21/2010 09:25am

    All articles will probably be in VS2010 and that one was--the RC version. I must not have mentioned it. Sorry.

    Reply
  • just for the record, one must escape as > and <

    Posted by mkamoski on 04/21/2010 09:20am

    ...just for the record, one must escape as > and < ...
    ...and check the "pre-formatted" box...
    ...if one wants to dispaly those as actuals...
    ...just fyi...

    Reply
  • ug one more time, just because

    Posted by mkamoski on 04/21/2010 09:19am

    This... 
    <Extension()>
    ...should be this
    <Extension()> _
    ...so maybe this will post this time without stripping out the greater-than and less-than characters and everything in between, I am escaping them now...

    Reply
  • Great article, minor code tweak possibly needed

    Posted by mkamoski on 04/21/2010 09:15am

    This... ...probably should be this... _ ...with the line continuation character. That is minor-- so, this is a REALLY great article. Thank you. -- Mark Kamoski

    • let's try that comment again with source code this time

      Posted by mkamoski on 04/21/2010 09:17am

      The editor stripped out my code sample so here goes again.
      ...this...
      
      ...should probably be this...
       _
      ...so that is that.

      Reply
    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: November 20, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT Are you wanting to target two or more platforms such as iOS, Android, and/or Windows? You are not alone. 90% of enterprises today are targeting two or more platforms. Attend this eSeminar to discover how mobile app developers can rely on one IDE to create applications across platforms and approaches (web, native, and/or hybrid), saving time, money, and effort and introducing apps to market faster. You'll learn the trade-offs for gaining long …

  • IBM Worklight is a mobile application development platform that lets you extend your business to mobile devices. It is designed to provide an open, comprehensive platform to build, run and manage HTML5, hybrid and native mobile apps.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds