CSC/ECE 517 Fall 2009/wiki2 5 jn: Difference between revisions
InteliSavant (talk | contribs) mNo edit summary |
InteliSavant (talk | contribs) mNo edit summary |
||
Line 17: | Line 17: | ||
=Terminology= | =Terminology= | ||
*'''Reflection''' - the process by which a computer program can observe and modify its own structure and behavior. | *'''[http://www.google.com/url?q=http://en.wikipedia.org/wiki/Reflection_(computer_science) Reflection]''' - the process by which a computer program can observe and modify its own structure and behavior. | ||
*'''MetaProgramming''' - the writing of computer programs that write or manipulate other programs (or themselves) as their data. | *'''[http://www.google.com/url?q=http://en.wikipedia.org/wiki/Meta_programming MetaProgramming]''' - the writing of computer programs that write or manipulate other programs (or themselves) as their data. | ||
*'''MetaData''' - "data about data". Describes how and when and by whom a particular set of data was collected, and how the data is formatted. | *'''[http://www.google.com/url?q=http://www.geospatial.govt.nz/glossary/ MetaData]''' - "data about data". Describes how and when and by whom a particular set of data was collected, and how the data is formatted. | ||
*'''Binding''' - an object that encapsulates the execution context at some particular place in the code. | *'''[http://www.google.com/url?q=http://en.wikipedia.org/wiki/Binding_(computer_science) Binding]''' - an object that encapsulates the execution context at some particular place in the code. | ||
*'''Instance''' - an object with functionality and information described by a class. | *'''[http://www.google.com/url?q=http://en.wikipedia.org/wiki/Instance_(programming) Instance]''' - an object with functionality and information described by a class. | ||
*'''Scope''' - a location in a program where the value of the variable is available. | *'''[http://www.google.com/url?q=http://en.wikipedia.org/wiki/Scope_(programming) Scope]''' - a location in a program where the value of the variable is available. | ||
Revision as of 19:58, 13 October 2009
Reflection APIs (TOC)
Introduction
What is Reflection?
Reflection in programming is a technique in which programs are able to dynamically view and alter their own structure and flow. For example, in reflection one could determine method/variable/class names, add or delete method/variables, alter code to current methods, or simply alter values in variables. Reflection is a subset of MetaProgramming which relies heavily on the use of meta-information. Meta-Information is used to keep track of the program structure such as names of available methods, variables and classes but also is what really makes reflection possible. In theory there are nearly no bounds to reflection as it directly modifies the binary of the program. The current limitations of this process are related to the APIs available for specific languages. Majority of object-oriented languages have the functionality built in and some like C++ require external libraries to achieve even the basic functionality of Reflection.
What languages are Supported?
Due to the complexity required to support this majority of the languages that support this feature tend to be object oriented. Some examples of supported languages are Java, Ruby, C#, Perl, PHP, JavaScript, Python, and ActionScript. In this page we will cover a select few of the languages above and what native API support is avaliable for each of the languages.
Terminology
- Reflection - the process by which a computer program can observe and modify its own structure and behavior.
- MetaProgramming - the writing of computer programs that write or manipulate other programs (or themselves) as their data.
- MetaData - "data about data". Describes how and when and by whom a particular set of data was collected, and how the data is formatted.
- Binding - an object that encapsulates the execution context at some particular place in the code.
- Instance - an object with functionality and information described by a class.
- Scope - a location in a program where the value of the variable is available.
Reflection in C#
Reflection in C# is included in the .NET framework provided by microsoft. The methods and classes usable for reflection are access directly from the System.Reflection namespace and support any class type available under the .NET framework. The basis of reflection in C# is powered by the Type class which is the way metadata is accessed. This type object gives information about all available methods, variables, constructors, and more from a given class.
Reflection in C# is not as dynamic as some other languages in the fact that it doesn't support the alteration of class variables, methods, and attributes. As shown above the reflection implementation in C# is more for data collection and analysis for object types. One of the best applications for Reflection in C# is the flexibility to dynamically load assembly code at runtime, ascertain information about the methods, variables and constructors, and then invoke code directly. This type of usage makes examples especially hard to include in a short description as this. For this reason the example has been simplified and is displayed below.
Example
public class Counter { // Private Variable private int ctr; // Constructor public Counter(int temp) { ctr = temp; } // Increment public int Inc(int temp) { return ctr+=temp; } // Decrement public int Dec(int temp) { return ctr-=temp; } }
Suppose we have the basic class as listed above. There is only one constructor, one private instance variable, and two functions which increment or decrement the instance variable by a certain amount. We could easily begin obtaining information about this class by performing the code below.
Counter myCtr = new Counter(1000); Type tempType = myCtr.GetType();
From the instance of myCtr we were able to use the GetType method to instantiate and parse meta-information about the Counter class. Keep in mind that this class could have been invoked at compile time and that we may have no previous information about it. We could then dynamically determine what constructors and methods were available by performing the following actions.
ConstructorInfo [] c_info = tempType.GetConstructors(); MethodInfo [] m_info = tempType.GetMethods();
From these two lines of code we now have an array of information for both the available methods and the available constructors. We can use the information in these two types to dynamically incorporate the class into the system at runtime.
C# Conclusions
The included reflection API in the C# language creates a way for programmers to dynamically retrieve information from external assembly code. They can then proceed to use that information to access the types available from the assembly. While this is a difficult concept to grasp it does have its usages. In the System.Reflection.Emit namespace there is a way to dynamically create types. This form of reflection is typically used by script engines and compilers as they import external types to be used in the current binaries.
Reflection in Java
Reflection in Java is much like the reflection available in C#. Java's Reflection API is located in the java.lang.reflect package and makes it possible for a programmer to dynamically parse information about classes, interfaces, methods, and fields at runtime. With this information it is possibly to dynamically incorporate these new classes into the running application. Probably one of the best examples that this could be used for is the Netbeans compiler. Netbeans is actually a java application which runs and compiles user's java applications. Once the user chooses to run it Netbeans then dynamically parses the classes and fields, then loads the class to be run.
Java actually has a few advantages over C# in that it can modify access to class fields and methods. Once it has an instance of the class it can manually override the access and begin calling private and protected functions that users typically wouldn't be able to do. This could be very useful for performing unit tests on a class since you now have access to all methods and fields. An example of how both are performed is shown below.
Example 1
public class testClass { // Private Variable private String privateString = null; // Constructor public testClass(String temp) { privateString = temp; } // Private Method private String getString(){ return privateString; } }
Suppose we had the simple class shown above. In this class we have both a private field and a private method which we wish to access to perform unit testing on. Normally this could only be possible by creating public methods to call the private functions and getters and setters to change the fields. With reflection we could perform the following.
// Create/Get an instance of the class. testClass m_Class = new testClass("MyString"); // Get an instance of the field within the class Field m_Field = testClass.class.getDeclaredField("privateString"); // Remove the access restricition on the variable m_Field.setAccessible(true); // Print the value of the field for a specific instance. System.out.println((String) m_Field.get(m_Class));
The process of accomplishing this seems a bit daunting especially when you compare it to Ruby's implementation which is described later. But the result is direct access to instance variables at run-time no matter what the restriction policy. If we needed to access the private method we could accomplish that as shown below.
// Create/Get an instance of the class. testClass m_Class = new testClass("MyString"); // Get an instance of the method within the class Method m_Method = testClass.class.getDeclaredMethod("getString", null); // Remove the access restricition on the method m_Method.setAccessible(true); // Print the result of method for a specific instance. System.out.println((String) m_Method.invoke(m_Class, null));
The result is the same as from the previous section of code. We simply accessed the private getter rather than calling the instance variable directly.
Example 2
In the C# example we were not able to present any code for dynamically loading classes into the running program due to the complexity and length. Java has made this process relatively simple and only requires a few lines of code. Below is how we could dynamically load our previous class example from an outside source.
public class test2Class {
// Main Method of invokiing class public static void main(String[] args){ // Instance of a classloader as part of this class ClassLoader m_Loader = test2Class.class.getClassLoader(); // Try/Catch Block try { Class m_Class = m_Loader.loadClass("com.myClassPackage.testClass"); System.out.println("Successfully loaded class: " + m_Class.getName()); } catch (ClassNotFoundException e) { System.err.println(e) }
}
The result from above is "testClass" as it loaded our example class from before. From here we could now instantiate the class and call its functions (or search for what functions it has to offer). This gives real insight to the flexibility of Java's Reflection API and how it could be practically used.
Java Conclusions
Java's Reflection API supplies a great deal of flexibility. It allows the user almost complete access to classes as well as the ability to dynamically load new classes and utilize their functions. While some of the method calls aren't trivial majority of the coding is understandable and very useful once you understand what is going on. Java also has the added advantage to access private functions and variables of a class instance which can allow easier unit testing.
Reflection in Ruby
The nature of Ruby as an object-oriented language gives great support for Reflection. The big advantage to ruby is it gives us complete flexibility and control with reflection. Most usages of reflection is only to obtain information and create new types dynamically but not modify current ones. With Ruby's reflection api we have the ability to add or remove almost anything, whether that's a method, a class, or even class variables. Please note that this flexibility can also lead to a programs downfall as with any reflection API. Too much alteration of the source code can cause many unknown bugs.
The current support for reflection in ruby is built into the language and is accessible through certain functions within the standard Ruby API. The methods most commonly used for reflection in Ruby along with some basic examples of their usage are shown below.
Eval Method
The eval method is defined in the Kernel Module of Ruby and is used to evaluate literals encompassed in strings. This may not seem useful but the eval method also work in the scope of bindings which represent the scope of execution at a particular place in the code. This allows programmers to view any variables in any scope for any instance. An example of eval's usage is shown below.
Example
class Test def initialize(var) @myvar = var end def getBinding return binding() end end test1 = Test.new("12+5") #We can determine what test1's instance of myvar is and evaluate it without an accessor method eval("@myvar", test1.getBinding) #=> 17
I this example we can see that by acquiring the binding at some scope in the program we can use it to get inside the context of any object instance and determine the value of any instance variables.
Instance Eval Method
The instance_eval method is defined in the Object class and works within the instance of any class. This method performs much of the same operations as the eval method but works in the scope of an object without requiring the binding statement as above. In fact we can accomplish the same example as above with the code shown below.
Example
class Test def initialize(var) @myvar = var end end test1 = Test.new("12+5") #We can determine what test1's instance of myvar is and evaluate it without an accessor method test1.instance_eval { @myvar } #=> 17
Again this code performs exactly the same thing as the prior example above. The result is 17 which is the evaluation of the string literal as a command.
Class Eval Method
The class_eval method is defined in the Module class and works within the instance of any class. This method can be used to work within the context of any object whether that is assigning or accessing variables, declaring instance methods, or even re-defining inherited methods. The method takes actions either via string literal or a block of code. This method is one of the strongest and most versatile methods in the Ruby Reflection API since it can access any private methods of any objects and its parents, this giving you full control over control.
Example 1
class Test def initialize(var) @myvar = var end end test1 = Test.new("12+5") #We can determine what test1's instance of myvar by creating an accessor method for this instance. test1.class_eval{ def getVar; @myvar; end } eval(test1.getVar) #=> 17 #Or by simply accessing the local variable as below. eval(test1.class_eval{@myvar}) #=> 17
This example shows the versatility of the class_eval method to work on any class instance. It performs many of the operations that the previous methods have performed but with the added advantage of assigning instance methods.
Example 2
class Test def initialize(var) @myvar = var end private: def increment @myvar += 1 end end test1 = Test.new(17) #We can directly use class_eval to call private methods within a class as well. test1.class_eval{ increment() } #=> 18
This example shows that class_eval has even more flexibility to call private functions of a class from outside of the class scope.
Example 3
class Test def initialize(var) @myvar = var end end test1 = Test.new(17) # We can directly use class_eval to add variables to the instance of the class test1.class_eval{ instance_variable_set :@myvar2 , 25 } test1.class_eval{ @myvar2 } #=> 25 # Or We can remove variables from an instance of the class test1.class_eval{ remove_instance_variable :@myvar2 } test1.class_eval{ @myvar2 } #=> nil
This example shows that class_eval is capable of defining and removing instance variables from the instance of a class.
Define Method Method
The define_method method is defined in the Module class and works within the instance of any class. This method can be used to work within the context of any object to declare methods directly. This method can be accomplished by using the class_eval method but is also a bit more flexible in it's notation. An example of the use of this function is shown below.
Example
class Test def initialize(var) @secret = n end end test1 = Test.new("This is my String!") #We can use define_method to create a instance method for this class with multiple parameters. test1.class_eval{ define_method(:getSubString){|start, end| @secret[start,end]} } test1.getSubString(5,9) #=> "is my"
This example shows how to use define_method to create an instance method with multiple parameters by the common block notation. Please not that we did have to use class_eval still to access the define_method. This was due to the fact that define_method is a private method within the Module class.
Ruby Conclusions
The ruby language provides great support for reflection due to the nature of its design. By using the class_eval method by itself one could completely encompass majority of the required reflection programming. The other functions do provide some other basic support not granted to the class_eval function, specifically the eval function in combination with bindings. The use of bindings allow the program to save the context of scope at some particular moment in the program and access it later. With these functions ruby makes reflection simple.
Conclusions
All of the Reflection API's had one thing in common. The ability to analyze classes and determine information about them from the available metadata. C#'s API allows the usage of dynamically loaded classes but does not seem as trivial to use as the other APIs. Java has take it a step further and allows instance variables to be monitored during execution as well as the ability to call classes' private functions as needed. Java has proven to be a very flexible reflection API.
The most flexible Reflection API by far is Ruby's. The nature of Ruby's language allows it to dynamically create and destroy classes as well as variables and methods within the class. No other language that we've seen will allow you to add or remove functionality of a class as needed during execution. This allows Ruby programmers to invent new and interesting uses for this flexibility when designing their code.
Links
[2] Reflection in C# - CoderSource.net
[3] MSDN Visual C# - Reflection Programmers Guide
[4] Ruby Reflection
[5] A Ruby Metaprogramming Introduction
[6] Ruby Programming Language - Reflection and Metaprogramming