CSC/ECE 517 Fall 2009/wiki2 5 ReflectionApis

From Expertiza_Wiki
Revision as of 23:23, 9 October 2009 by Sganaga (talk | contribs)
Jump to navigation Jump to search

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 o-o 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 lok 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 .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 do'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 wrt 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

A simple example to list the hierarchy is shown below for a class called Demo. This example is borrowed from RubyCentral.

class1 = Fixnum begin

 print class1
 class1 = class1.superclass
 print " < " if class1

end while class1 puts p Fixnum.ancestors


Ruby can also separately extract private, protected and public methods. An API also exists to extract a singleton method.

 private
   def privMethod
   end
 protected
   def protMethod
   end
 public
   def pubMethod
   end
 def Demo.classMethod
 end
 CONST = 1.23

In this case,

Demo.private_instance_methods » ["privMethod"] Demo.protected_instance_methods » ["protMethod"] Demo.public_instance_methods » ["pubMethod"] Demo.singleton_methods » ["classMethod"] Demo.constants - Demo.superclass.constants » ["CONST"]

</source>

Conclusion

         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.

See also

•  For Beginners
      The high level overview of SCM best practices.

•  For Advanced Readers
      An 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.
      This is the 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.

•  For Developers
      The IBM Rational ClearCase Version Control tool document contains tutorials and examples that are easy to follow.
      Information about SVN and its free download versions by O'Reilly media can be found here


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