CSC/ECE 517 Fall 2009/wiki2 5 jn
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 avaliable 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
- MetaProgramming
- MetaData
- Binding
- Instance
- Scope
Available Reflection APIs
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 avaliable 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 avaliable methods, variables, constructors, and more from a given class.
Reflection in C# is not as dynamic as most other languages in the fact that it doesn't easily support the alteration of class variables, methods, and attributes. As shown above the reflection implementation in C# is more for data collection and anaysis for object types. One of the best applications for Reflection in C# is the flexibility to dyniamically load assembly code at runtime, ascertain information about the methods, variables and constructors, and then invoke code from the assembly directly. This type of usage makes examples especially hard to include in a short diescription as this. For this reason the examples have 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 avaliable 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 infmoration for both the avaliable methods and the avaliable constructors. We can use the information in these two types to dynamically incoroporate the class into the system at runtime.
Conclusion
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 avaliable 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 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 flexibiliy 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 accessable through certain fuctions 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 encompased 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) @secret = n end def getBinding return binding() end end test1 = Test.new("12+5") #We can determine what test1's instance of secret is and evaluate it without an accessor method eval("@secret", 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) @secret = n end end test1 = Test.new("12+5") #We can determine what test1's instance of secret is and evaluate it without an accessor method test1.instance_eval { @secret } #=> 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) @secret = n end end test1 = Test.new("12+5") #We can determine what test1's instance of secret by creating an accessor method for this instance. test1.class_eval{ def getSecret; @secret; end } eval(test1.getSecret) #=> 17 #Or by simply accessing the local variable as below. eval(test1.class_eval{@secret}) #=> 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) @secret = n end private: def increment @secret += 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) @secret = n 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 :@secret2 , 25 } test1.class_eval{ @secret2 } #=> 25 # Or We can remove variables from an instance of the class test1.class_eval{ remove_instance_variable :@secret2 } test1.class_eval{ @secret2 } #=> 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.
Conclusion
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 specifically 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.
Reflection in PHP
Reflection in PERL
Reflection in ActionScript
Conclusions
Links
[2] Reflection in C# - CoderSource.net
[3] MSDN Visual C# - Reflection Programmers Guide
[4]