CSC/ECE 517 Fall 2009/wiki2 5 ReflectionApis: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
No edit summary
 
(79 intermediate revisions by 2 users not shown)
Line 1: Line 1:
<b> Best Practices for Source Code Management with Version Control</b>
<b> Reflection APIs </b>


----
----
<br>
= Introduction =
Reflection is a programming technique used to build code that is flexible by giving the programmer the power to modify and examine the behaviour of a program at runtime. Reflection gives the code access to internal structures of classes - constructors, fields and methods. It falls under the broad area of Metaprogramming - programs that write other programs.Reflection can be very powerful for late binding, creating new types duing run-time, examining types etc.
This wiki will walk you through the facilities provided by specific languages for using reflection. It does NOT list every API in the language. It answers the following questions: Is reflection "built in" to some object oriented languages or do languages require external libraries to perform reflection? What built-in and library functions are provided? Which APIs are easiest to use and why?.
= Reflective Languages =
The concept of reflective programming was started late back in the 80's. Since then, there have been many languages that provide reflection capabilities. Reflection requires dynamic modification of the program. For this reason, compilers would have to have some metadata format to look back at it's own code. Java and C# do this through the Java virtual machine and the .NET VM respectively. On the other hand, since dynamic languages like Python and Ruby only try to interpret the code at run-time, more powerful forms of reflection can be achieved. Other languages like C, C++ lack reflection capabilities largely because of the metadata that they fail to maintain. The following are a few languages that support reflection:


= Introduction =
&nbsp;&nbsp;&nbsp;&bull; Java<br>
&nbsp;&nbsp;&nbsp;&bull; C#<br>
&nbsp;&nbsp;&nbsp;&bull; Ruby<br>
&nbsp;&nbsp;&nbsp;&bull; Smalltalk<br>
&nbsp;&nbsp;&nbsp;&bull; Python<br>
&nbsp;&nbsp;&nbsp;&bull; Eiffel<br>
 
<br>
= Is reflection "Built-in"? =
 
Reflection naturally comes to interpreted languages. Ruby, Python, Smalltalk would not have to go out of their usual way to implement and exhibit reflective properties. On the other hand, Java and .NET look at the intermediate code to figure out the structure of a class and then try to add more at run-time. This, obviously takes a performance hit since the code cannot be compiled and optimized. Java and Microsoft .NET both have references to objects as well as built-in types. Wrappers for built-in types exist that provide reflection on primitive types.
 
Languages like C, C++ lack reflective capabilities. They are based on the philosophy of "You don't pay for what you don't use". Maintaining information about types, methods and the like involve significant overhead. Moreover, performance can become a bottleneck, not to mention a few security concerns with reflection. C and C++ compilers optimize code to maximize performance and are not open to trade performance and overhead. If reflection is deemed necessary for such languages, there would have to be  another library or a tool to take care of parsing the source code, building abstract syntax trees containing every detail of the program, build symbol tables, extract control and data flows by building a graphical representation and have custom analysers analyse this representation. The DMS Software Reengineering Toolkit is one such toolkit for C, C++ and COBOL.
 
<br><br>
= Programming Language APIs =
 
We first look at capabilities provided by compiled languages like Java and C# for reflection. We then move on to dynamic languages like Python and Ruby. Reflection in Java and C# are said to be more verbose than in Python or Ruby.
 
== Java ==
 
The java virtual machine is capable of generating an instance of java.lang.Class for every object created. This Class consists of methods that can examine the properties of the object at runtime. In java, all the reference types are inherited from the Object class. The primitive types on the other hand have wrapper classes around them - for example int has the Integer class for it's wrapping. For example, to get the name of a class:
 
 
  public static void main(String[] args) {
    Class c = java.lang.Integer.class;
    String info;
    info = c.getName();   
    System.out.println(info); //Prints out java.lang.Integer
  }
 
Similarly, an example to find out about all the methods in a class and method parameters is listed below
 
  public class testMethod {
      int func1(
      Object obj) throws NullPointerException
      {
        if (obj == null)
            throw new NullPointerException();
        return 1;
      }
       
      public static void main(String args[])
      {
        try {
          Class c1 = Class.forName("testMethod");
       
            Method mtd[]
              = cls.getDeclaredMethods();
            for (int i = 0; i < mtd.length;
              i++) { 
              Method m = methlist[i];
              System.out.println("name
                = " + m.getName());
              System.out.println("decl class = " +
                              m.getDeclaringClass());
              Class params[] = m.getParameterTypes();
              for (int j = 0; j < params.length; j++)
                  System.out.println("
                  param #" + j + " " + params[j]);            }
        }
        catch (Throwable e) {
            System.err.println(e);
        }
      }
  }
 
<br>Before using reflection, Java requires the user to import java.lang.reflect.*; This is the package that provides reflective capabilities. Java provides APIs for Retrieving class objects, Examining class modifiers and types and discovering class members.  What Java lacks however is the finer granularity when compared to .NET. The Class objects that java uses tend to lose some information, for example, parameter names.
 
Java's reflection also includes useful methods that return classes themselves. The complete design of all the related classes can be captured using APIs such as:
 
&nbsp;&nbsp;&nbsp;&nbsp;&bull;getSuperClass()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;: To return the classes' super class<br>
&nbsp;&nbsp;&nbsp;&nbsp;&bull;getClasses()&nbsp;;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;: Return all members including inherited members<br>
&nbsp;&nbsp;&nbsp;&nbsp;&bull;getEnclosingClass()&nbsp;: used in case of nested classes. Returns the immediate enclosing <br><br>


&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Reflection is a programming technique used to build flexible code. [[Image:Version_Control.jpeg|thumb|center|550px|alt=An example of version control|An example of version control]]
The APIs to extract and modify Fields, methods and constructors can be found here[http://java.sun.com/docs/books/tutorial/reflect/member/index.html]. More information about Java's Reflection APIs along with examples can be found here[http://java.sun.com/docs/books/tutorial/reflect/index.html]


=Terminology =
== C# ==
<b>&bull; Repository - &nbsp;</b> Store house for files. Usually integrated with a database<br>
<b>&bull; Trunk - &nbsp;</b>This is the location where the source code can be found. It is at the root of the tree<br>
<b>&bull; Working set - &nbsp;</b> Downloaded copy of the codebase<br>
<b>&bull; Client - &nbsp;</b> Application that connects to the repository<br>
<b>&bull; Server - &nbsp;</b>The system that hosts the repository<br>
<b>&bull; Code Check-out - &nbsp;</b>Downloading a file or a set of files from the codebase to a workspace in order to run / modify the code
<br><b>&bull; Code Check-in - &nbsp;</b>Uploading new / modified files to a codebase from a workspace
<br><b>&bull; Branching - &nbsp;</b>A technique used to aid the concurrent development of software. Simply put, it allows for development of code simultaneously by creating multiple paths for development.A &nbsp;&nbsp;branch is for a single logical change in the code
<br><b>&bull; Codeline - &nbsp;</b>A codeline is similar to a branch but can support multiple logical changes to the code. Often, branch and codeline are used interchangeably
<br><b>&bull; Merging - &nbsp;</b>Process of integrating the changes in the code with the codeline. For example, if A branches out from a codeline, development continues on the codeline. A would then have &nbsp;&nbsp; to merge his / her changes with the codeline to keep the versioning most recent.


= Motivation for Source code management (SCM) =
C# differs from Java in the way it handles reflection. While Java works at the class level, C# goes a level further to implement reflection at the assembly level. These assemblies are nothing but .DLL files. Thus, to make a long sentence short, Java looks at the .class file while C# looks for the corresponding DLL file.


&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;In a development environment, a single project can simultaneously be in multiple phases. One team would be working on building new features into the product. A second team would be working on fixing bugs (Tech support) while a third team concentrates on prototypes for future development. Different lines are created for each of these items – Functional line, development line, maintenance line, release line, integration line and so on. In such an environment, management of the codebase becomes critical for the following reasons
C# also has a unique class called ParameterInfo which contains more metadata information. While java rips off some items like parameter names, ParameterInfo strives to hold all this information thus allowing for finer granularity reflection. Apart from these there are no major differences between the way reflection is handled in Java and C#.


  1. Allow simultaneous / parallel development of software
The functionality of the APIs available in C# closely resemble the ones in Java differing in their hierarchies and names. Here is a small example that shows the C# way of reflecting on an assembly.
  2. Integrate code changes from different teams / developers
  3. Propagate bug fixes to future versions of the software
  4. Isolate, coordinate and tidily separate work item units
  5. Track and revert to older versions
  6. Keep related projects in sync with one another
  7. Reduce costs of late merging
  8. Reduce cost of maintaining codebases


= Efficient Source Code Management (SCM) =
    using System;
    using System.Reflection;
    public class ReflectMe
    {
        public static void Main(  )
        {
            Assembly assem = Assembly.Load("Mscorlib.dll");
            Type[] assemTypes = assem.GetTypes();
            foreach(Type t in assemTypes)
            {
                    Console.WriteLine("Type =>{0}", t);
            }
            Console.WriteLine(assemTypes.Length);
        }
    }


&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;There are a lot of challenges associated with Source Code Management(SCM) and there exists no standard way for doing SCM. When do you create new branches? When do you create new codelines? Who should be responsible for the branch? When do you merge code? How do you integrate code fixes with future releases? How do you keep track of multiple releases? These are questions that have no definite answers. Each project uses a different  SCM technique depending on development cycles, releases, bug fixes and size of the project.
APIs to manipulate fields, methods within the class can be found at the Microsoft documentation site[http://msdn.microsoft.com/en-us/library/ms173183(VS.80).aspx]


&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Source code management techniques invariably include trade-offs. While branching early could be good, the costs for merging could add up. Trade-offs between maintaining multiple lines like development and maintenance lines can also be confusing to an extent. Optimistically thinking, maintaining a single line for both maintenance and development could be tempting. It would allow new changes / bug fixes from the maintenance team to become immediately visible in the development line thus incorporating all the bug fixes into the new development cycles. However, the cost of merging each time into the new development branch is an important criterion. What would happen if the new code being developed by the development branch is in conflict with a new bug fix? Who would re-work the code to get around the problem? Such trade-offs and many many more become important for efficient source code management.
== Ruby ==
Dynamic languages like ruby have the ability to introspect - i.e examine snippets of the program within the program itself. While Java and C# call this reflection, Ruby calls it introspection. There is not a big difference in ruby with respect to compile time or run-time. Code can be ynamically added to a running process. Thus, unlike Java or C#, there is no observable affect in performance.


&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Fortunately, there a few common guidelines and patterns that allow for efficient management of source code. The following section describes a few common guidelines in a typical scenario
Ruby can list all the methods in an object or even check whether an object responds to a particular method using the respond_to? API call. Similarly looking at class hierarchies is extremely simple - .superclass and .ancestors list the hierarchy. Fixnum.ancestors produces the following output:


  1. Have a main line from which all the code is derived
>> Fixnum, Integer, Precision, Numeric, Comparable, Object, Kernel
      The main line sets up the base of the project. Typically, when some code is being developed for two different platforms, say,  <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Windows and Linux, common modules are placed in the main line. The two codelines for Linux and Windows are then taken    <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from the main line to create parallel development lines.
  2. Have parallel maintenance and development lines
      Product development and maintenance (eg: bug fixes) happen simultaneously. For this reason, we need two parallel lines and <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;these lines should interact closely and integrate / merge frequently to stay up-to-date with the fixes
  3. Have one codeline per release
      Separation / Isolation of the codeline for a specific release becomes important since parallel work goes on on the previous <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;version of the software(maintenance). Thus a new codeline should be drawn whenever a release for the product is planned
  4. Create policies for each codeline
      Policies for a codeline define the stability and maintainability of a source code management system. The policy for the <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;development line could encourage late merges while those for bug fixes encourage merging often.
  5. Merge early and often
      As far as possible, developers must be working with the latest copy of the code. Thus, merging often becomes important. <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Early merges help in having common bases for further development since the early parts of the code are most critical. Frequent <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;merging also helps in avoiding large merges later in the cycle which could lead to incosistent code. It is the best way to keep <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;all developers in sync with the product code..
  6. Do not isolate too much,  
      Isolation could be beneficial in case of large projects. However, creating many codelines / branches is inadvisable.  The <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cost for merging could become high.
  7. Analyse and realign
      The versioning tree could grow out of bounds and become wider and wider. Wider the tree, the more codelines / branches it <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;has and the more difficult it is to manage. SCM systems must be checked often for such widening trees and action for merging <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;codelines must be taken appropriately to sync up versions
  8. Have an owner for every codeline
      Ownership is a key term used in source code management. Every codeline is assigned an owner who is responsible for the codeline. <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The owner's typical tasks would be to assist in code integration and changes in his codeline, clarify ambiguous code policies,<br>&nbsp;&nbsp;&nbsp;&nbsp; decide when to freeze and unfreeze code, co-ordinate across teams to make successful merges.


Ruby can also separately extract private, protected and public methods. An API also exists to extract a singleton method. An example to illustrate this for a class called Demo is shown below. This example is borrowed from RubyCentral.
  private
    def privateMethod
    end
  protected
    def protectedMethod
    end
  public
    def publicMethod
    end
  def Demo.classMethod
  end


&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The above guidelines help in improving manageability of the source code versions. It leads to increased coordination among developers, improves traceability, isolates changes, defines definite roles and responsibilities and reduces complexity.
In this case, the methods can be listed as follows


= Examples =
Demo.private_instance_methods » ["privateMethod"]<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IBM Rational ClearCase is a widely used version control software with a secure version management and easy to follow interface. It follows the guidelines of the best practices for source code management with version control. It centralizes the code, making it accessible to anyone in the software development team for a particular development. The ClearCase software supports parallel development and easy merging techniques. One of the most useful features of IBM Rational ClearCase is the views. It provides two different views, SnapShot and Dynamic. SnapShot views help to create local copy of the code from the codebase while the dynamic one is used when the codebase has to be modified (during merge). This feature helps to maintain the version of the code by allowing the developer to choose the level of access required for the code.  
Demo.protected_instance_methods » ["protectedMethod"]<br>
Demo.public_instance_methods » ["publicMethod"]<br>
Demo.singleton_methods         » ["classMethod"]


&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;As discussed in the [[#Efficient Source Code Management (SCM)|previous section]], the ClearCase maintains the main branch as the baseline and when a new project has to be derived from the main branch, a sub-branch is created which uses the baseline. This sub branch is used to build the project on. Every developer who is a part of the project team and for whom the project's main branch is accessible is given a unique id. Whenever a file is checked out by a developer, his name appears on the ClearCase view. The rest of the team can always see which file is being modified / looked at by which team members. This feature sometimes helps the developer to decide whether he should access the file to modify it now or not. Also, the ClearCase maintains every version of a file. Every time a file is checked out, a copy is maintained, so that when the modified version of the file is checked in, the view still has the old version of the file as well. One of the most powerful features of this tool is that every time a project is successfully completed and needs to be released to make it accessible to the other projects (branches of the main branch), it can be merged with the main branch which makes it accessible to all the other branches.<br>
More APIs to peek into objects and classes and manipulate classes on the fly can be found at RubyCentral[http://www.rubycentral.com/pickaxe/ospace.html] Reflection or Introspection can be easier and effective with an interpreted language like Ruby. The APIs are definitely simpler and cleaner to use than Java or C#.


&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The following diagram is taken from the [http://publib.boulder.ibm.com/infocenter/cchelp/v7r0m0/index.jsp?topic=/com.ibm.rational.clearcase.hlp.doc/cc_main/how_base_populate.htm IBM Boulder site]. It is a snapshot of the ClearCase.
== Python ==
[[Image:Clearcase.gif|thumb|center|550px|alt=A snapshot of IBM Rational ClearCase|A snapshot of IBM Rational ClearCase]]
Python has excellent support for reflection, or introspection as it is called in interpreted languages. Python can be as effective as ruby in introspecting code. The type function, str Functiona and other built-ins add up together to make introspection extremely powerful in Python.
<br>The above diagram is a snapshot view of a shared file Prog.c which is in the clearcase. The circles show the different versions of the file, i.e. the number of circles are basically the number of times the file was checked out and checked back in. The dark circle means that the file has been currently checked out by someone called "user" as shown. The file Prog.c, when checked out is copied locally in the user's machine. The changes that the user makes remains local until he checks in the file again.


The details of the tool can be found in the IBM publication cited in the [[#References|References]] section of this page.
Python can use getattr to find out about modules. The following example illustrates usage of the getattr functionality


&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Another widely used tool for source code management is Subversion - an open source initiative. Subversion has superseeded CVS as the versioning control system of choice since it includes important functionality like deleting and renaming directories which were absent in CVS. The users of SVN range from those working on very small classroom projects to mammoth ones like the ones in SUN Microsystems. SVN uses a relational database, BerkleyDB as the backend and works much faster than CVS. With space for adding metadata with each file and support for "all or nothing" commits, SVN is more stable and allows for better management of the codebase.
  pythonfunc(){
li = ["Name1", "Name2"]
getattr(li, "pop")
getattr(li, "append")("Name3")
}


''The open source document of SVN can be found in the [[#References|References]] section of this page.''
The interesting point in Python is that getattr also works on Python modules. getattr can also be used as a dispatcher. For example, a single dispatch function can be used to pick from a list of functions for dispatch.  


In many ways, the support for reflection in Ruby and Python is similar. The syntax obviously differs and a detailed list and explanation of these can be found here[http://diveintopython.org/power_of_introspection/lambda_functions.html#d0e10403]
<br>
= Conclusion =
= Conclusion =
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Source Code Management or Revision Control is a way of helping the software development for faster and efficient delivery and reduce the amount of rework or manual code maintenance. In a typical development team setup, it becomes a critical tool which is used by every team member. Hence it becomes very important for the version control systems to be accurate and user friendly. Not only does it need to be simple to understand, but it also has to be reliable and transparent without any conflicts.
Reflection can be extremely powerful when used in the right amounts. Although language like Java and C# are verbose, they have good support for reflection. Ruby and Python being dynamically typed work wondefully well and are simpler to use. One major drawback of using reflection in java or C# is that the virtual machine starts interpreting code. This considerably slows down performance. The second drawback of reflection is wrt obscuring the code. Using too much of reflection can make the code obscure to the reader. If reflection is harvested in the right amounts, it can make the design more extensible while at the same time reducing programming overhead. Dynamically typed languages like Ruby and Python have simpler APIs and are hence easier to use


= See also =
= See also =
This section lists more resources that can be looked into for advanced reflection concepts in various languages
<b>&bull; &nbsp;C# </b><br>
http://msdn.microsoft.com/en-us/library/ms173183(VS.80).aspx <br>
Gives a comprehensive listing of APIs and short snippets on using the APIs
<b>&bull; &nbsp;Java </b><br>
http://java.sun.com/docs/books/tutorial/reflect/ <br>
A tutorial for using Reflection. Advantages, disadvantages and gotchas!


<b>&bull; &nbsp;For Beginners</b>
<b>&bull; &nbsp;Ruby </b><br>
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[http://www.perforce.com/perforce/papers/bestpractices.html The high level overview of SCM best practices].
http://phrogz.net/ProgrammingRuby/ospace.html <br>
<br><br><b>&bull; &nbsp;For Advanced Readers</b>
Shows the power of Ruby with Introspection. Code snippets to look inside classes, objects. Also lists performance considerations when using Ruby
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;An [http://hillside.net/plop/plop98/final_submissions/P37.pdf in-depth view of source code management using different patterns]. It helps in organizing related lines of development into appropriately diverging and converging streams of source code changes.
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;This is the [http://www.redbooks.ibm.com/abstracts/sg246399.html?Open&S_TACT=105AGX15&S_CMP=LP product RedBook] released by IBM on IBM Rational ClearCase. It explains the different features of their Version Control tool which follow the best practices discussed in this page.  
<br><br><b>&bull; &nbsp;For Developers</b>
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The [https://publib.boulder.ibm.com/infocenter/cchelp/v7r1m0/index.jsp?topic=/com.ibm.rational.clearcase.books.cc_build_windows.doc/cc_build.htm IBM Rational ClearCase Version Control tool document] contains tutorials and examples that are easy to follow.
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Information about SVN and its free download versions by O'Reilly media can be found [http://svnbook.red-bean.com/ here]


<b>&bull; &nbsp;Python </b><br>
http://www.ibm.com/developerworks/library/l-pyint.html <br>
A complete guide to Python Introspection. Help modueles for each functionality in Python Introspection.


= References =
= References =
1. http://www.sunsource.net/scdocs/ddCVS<br>
1. http://www.ibm.com/developerworks/library/j-dyn0603/<br>
2. http://www.codewalkers.com/c/a/Server-Administration/Source-Code-Version-Control-Solutions<br>
2. http://oreilly.com/catalog/progcsharp/chapter/ch18.html<br>
3. http://en.wikipedia.org/wiki/Revision_control
3. http://www.25hoursaday.com/CsharpVsJava.html#reflection<br>
4. http://www.rubycentral.com/pickaxe/ospace.html<br>
5. http://diveintopython.org/power_of_introspection/<br>
6. http://www.rubycentral.com/pickaxe/ospace.html

Latest revision as of 21:06, 14 October 2009

Reflection APIs



Introduction

Reflection is a programming technique used to build code that is flexible by giving the programmer the power to modify and examine the behaviour of a program at runtime. Reflection gives the code access to internal structures of classes - constructors, fields and methods. It falls under the broad area of Metaprogramming - programs that write other programs.Reflection can be very powerful for late binding, creating new types duing run-time, examining types etc.

This wiki will walk you through the facilities provided by specific languages for using reflection. It does NOT list every API in the language. It answers the following questions: Is reflection "built in" to some object oriented languages or do languages require external libraries to perform reflection? What built-in and library functions are provided? Which APIs are easiest to use and why?.

Reflective Languages

The concept of reflective programming was started late back in the 80's. Since then, there have been many languages that provide reflection capabilities. Reflection requires dynamic modification of the program. For this reason, compilers would have to have some metadata format to look back at it's own code. Java and C# do this through the Java virtual machine and the .NET VM respectively. On the other hand, since dynamic languages like Python and Ruby only try to interpret the code at run-time, more powerful forms of reflection can be achieved. Other languages like C, C++ lack reflection capabilities largely because of the metadata that they fail to maintain. The following are a few languages that support reflection:

   • Java
   • C#
   • Ruby
   • Smalltalk
   • Python
   • Eiffel


Is reflection "Built-in"?

Reflection naturally comes to interpreted languages. Ruby, Python, Smalltalk would not have to go out of their usual way to implement and exhibit reflective properties. On the other hand, Java and .NET look at the intermediate code to figure out the structure of a class and then try to add more at run-time. This, obviously takes a performance hit since the code cannot be compiled and optimized. Java and Microsoft .NET both have references to objects as well as built-in types. Wrappers for built-in types exist that provide reflection on primitive types.

Languages like C, C++ lack reflective capabilities. They are based on the philosophy of "You don't pay for what you don't use". Maintaining information about types, methods and the like involve significant overhead. Moreover, performance can become a bottleneck, not to mention a few security concerns with reflection. C and C++ compilers optimize code to maximize performance and are not open to trade performance and overhead. If reflection is deemed necessary for such languages, there would have to be another library or a tool to take care of parsing the source code, building abstract syntax trees containing every detail of the program, build symbol tables, extract control and data flows by building a graphical representation and have custom analysers analyse this representation. The DMS Software Reengineering Toolkit is one such toolkit for C, C++ and COBOL.



Programming Language APIs

We first look at capabilities provided by compiled languages like Java and C# for reflection. We then move on to dynamic languages like Python and Ruby. Reflection in Java and C# are said to be more verbose than in Python or Ruby.

Java

The java virtual machine is capable of generating an instance of java.lang.Class for every object created. This Class consists of methods that can examine the properties of the object at runtime. In java, all the reference types are inherited from the Object class. The primitive types on the other hand have wrapper classes around them - for example int has the Integer class for it's wrapping. For example, to get the name of a class:


 public static void main(String[] args) {
   Class c = java.lang.Integer.class;
   String info;
   info = c.getName();    
   System.out.println(info); //Prints out java.lang.Integer
 }

Similarly, an example to find out about all the methods in a class and method parameters is listed below

  public class testMethod {
      int func1(
      Object obj) throws NullPointerException
     {
        if (obj == null)
           throw new NullPointerException();
        return 1;
     }
       
     public static void main(String args[])
     {
        try {
          Class c1 = Class.forName("testMethod");
       
           Method mtd[] 
             = cls.getDeclaredMethods();
           for (int i = 0; i < mtd.length;
              i++) {  
              Method m = methlist[i];
              System.out.println("name 
                = " + m.getName());
              System.out.println("decl class = " +
                             m.getDeclaringClass());
              Class params[] = m.getParameterTypes();
              for (int j = 0; j < params.length; j++)
                 System.out.println("
                  param #" + j + " " + params[j]);            }
        }
        catch (Throwable e) {
           System.err.println(e);
        }
     }
  }


Before using reflection, Java requires the user to import java.lang.reflect.*; This is the package that provides reflective capabilities. Java provides APIs for Retrieving class objects, Examining class modifiers and types and discovering class members. What Java lacks however is the finer granularity when compared to .NET. The Class objects that java uses tend to lose some information, for example, parameter names.

Java's reflection also includes useful methods that return classes themselves. The complete design of all the related classes can be captured using APIs such as:

    •getSuperClass()     : To return the classes' super class
    •getClasses() ;          : Return all members including inherited members
    •getEnclosingClass() : used in case of nested classes. Returns the immediate enclosing

The APIs to extract and modify Fields, methods and constructors can be found here[1]. More information about Java's Reflection APIs along with examples can be found here[2]

C#

C# differs from Java in the way it handles reflection. While Java works at the class level, C# goes a level further to implement reflection at the assembly level. These assemblies are nothing but .DLL files. Thus, to make a long sentence short, Java looks at the .class file while C# looks for the corresponding DLL file.

C# also has a unique class called ParameterInfo which contains more metadata information. While java rips off some items like parameter names, ParameterInfo strives to hold all this information thus allowing for finer granularity reflection. Apart from these there are no major differences between the way reflection is handled in Java and C#.

The functionality of the APIs available in C# closely resemble the ones in Java differing in their hierarchies and names. Here is a small example that shows the C# way of reflecting on an assembly.

   using System;
   using System.Reflection;

   public class ReflectMe
   {
       public static void Main(  )
       {
           Assembly assem = Assembly.Load("Mscorlib.dll");
           Type[] assemTypes = assem.GetTypes();
           foreach(Type t in assemTypes)
           {
                   Console.WriteLine("Type =>{0}", t);
           } 
           Console.WriteLine(assemTypes.Length);
       }
   }

APIs to manipulate fields, methods within the class can be found at the Microsoft documentation site[3]

Ruby

Dynamic languages like ruby have the ability to introspect - i.e examine snippets of the program within the program itself. While Java and C# call this reflection, Ruby calls it introspection. There is not a big difference in ruby with respect to compile time or run-time. Code can be ynamically added to a running process. Thus, unlike Java or C#, there is no observable affect in performance.

Ruby can list all the methods in an object or even check whether an object responds to a particular method using the respond_to? API call. Similarly looking at class hierarchies is extremely simple - .superclass and .ancestors list the hierarchy. Fixnum.ancestors produces the following output:

>> Fixnum, Integer, Precision, Numeric, Comparable, Object, Kernel

Ruby can also separately extract private, protected and public methods. An API also exists to extract a singleton method. An example to illustrate this for a class called Demo is shown below. This example is borrowed from RubyCentral.

 private
   def privateMethod
   end
 protected
   def protectedMethod
   end
 public
   def publicMethod
   end
 def Demo.classMethod
 end

In this case, the methods can be listed as follows

Demo.private_instance_methods » ["privateMethod"]
Demo.protected_instance_methods » ["protectedMethod"]
Demo.public_instance_methods » ["publicMethod"]
Demo.singleton_methods  » ["classMethod"]

More APIs to peek into objects and classes and manipulate classes on the fly can be found at RubyCentral[4] Reflection or Introspection can be easier and effective with an interpreted language like Ruby. The APIs are definitely simpler and cleaner to use than Java or C#.

Python

Python has excellent support for reflection, or introspection as it is called in interpreted languages. Python can be as effective as ruby in introspecting code. The type function, str Functiona and other built-ins add up together to make introspection extremely powerful in Python.

Python can use getattr to find out about modules. The following example illustrates usage of the getattr functionality

pythonfunc(){
li = ["Name1", "Name2"]
getattr(li, "pop")
getattr(li, "append")("Name3")
}

The interesting point in Python is that getattr also works on Python modules. getattr can also be used as a dispatcher. For example, a single dispatch function can be used to pick from a list of functions for dispatch.

In many ways, the support for reflection in Ruby and Python is similar. The syntax obviously differs and a detailed list and explanation of these can be found here[5]

Conclusion

Reflection can be extremely powerful when used in the right amounts. Although language like Java and C# are verbose, they have good support for reflection. Ruby and Python being dynamically typed work wondefully well and are simpler to use. One major drawback of using reflection in java or C# is that the virtual machine starts interpreting code. This considerably slows down performance. The second drawback of reflection is wrt obscuring the code. Using too much of reflection can make the code obscure to the reader. If reflection is harvested in the right amounts, it can make the design more extensible while at the same time reducing programming overhead. Dynamically typed languages like Ruby and Python have simpler APIs and are hence easier to use

See also

This section lists more resources that can be looked into for advanced reflection concepts in various languages

•  C#
http://msdn.microsoft.com/en-us/library/ms173183(VS.80).aspx
Gives a comprehensive listing of APIs and short snippets on using the APIs

•  Java
http://java.sun.com/docs/books/tutorial/reflect/
A tutorial for using Reflection. Advantages, disadvantages and gotchas!

•  Ruby
http://phrogz.net/ProgrammingRuby/ospace.html
Shows the power of Ruby with Introspection. Code snippets to look inside classes, objects. Also lists performance considerations when using Ruby

•  Python
http://www.ibm.com/developerworks/library/l-pyint.html
A complete guide to Python Introspection. Help modueles for each functionality in Python Introspection.

References

1. http://www.ibm.com/developerworks/library/j-dyn0603/
2. http://oreilly.com/catalog/progcsharp/chapter/ch18.html
3. http://www.25hoursaday.com/CsharpVsJava.html#reflection
4. http://www.rubycentral.com/pickaxe/ospace.html
5. http://diveintopython.org/power_of_introspection/
6. http://www.rubycentral.com/pickaxe/ospace.html