• No results found

Reflection and Serialization 166 

In document CSharp Handout v1.0 (Page 166-170)

Learning Objectives

After completing this session, you will be able to: ‰ Acquire Class and Type Information ‰ Acquire Member Information from a Class ‰ Invoke dynamically methods from Classes

Reflection

You saw that a .NET application contains code, data, and metadata. Metadata is information about the data—that is, information about the types, code, assembly, and so forth—that is stored along with your program. Attributes are a mechanism for adding metadata, such as compiler instructions and other data about your data, methods, and classes, to the program itself. Attributes are inserted into the metadata and are visible through ILDasm and other metadata-reading tools.

An attribute is an object that represents data you want to associate with an element in your program. The element to which you attach an attribute is referred to as the target of that attribute. For example, the attribute:

[assembly: AssemblyKeyFile("c:\myStrongName.key")]

Inserts metadata into the assembly to designate the program's StrongName.

Reflection is the process by which a program can read a .NET assemblies metadata. The classes in the System.Reflection namespace, along with the System.Type and System.TypedReference classes, provide support for examining and interacting with the metadata.

Reflection is generally used for any of following tasks: ‰ Viewing metadata

‰ This might be used by tools and utilities that wish to display metadata. ‰ Performing type discovery

‰ This allows you to examine the types in an assembly and interact with or instantiate those types. This can be useful in creating custom scripts. For example, you might want to allow your users to interact with your program using a script language, such as JavaScript, or a scripting language you create yourself.

‰ Late binding to methods and properties

‰ This allows the programmer to invoke properties and methods on objects dynamically instantiated based on type discovery. This is also known as dynamic invocation. ‰ Creating types at runtime (Reflection Emit)

The ultimate use of reflection is to create new types at runtime and then to use those types to perform tasks. You might do this when a custom class, created at runtime, will run significantly faster than more generic code created at compile time.

Viewing metadata

You will use the C# Reflection support to read the metadata for a class. We start by initializing an object of the type MemberInfo. This object, in the System.Reflection namespace, is provided to discover the attributes of a member and to provide access to the metadata:

System.Reflection.MemberInfo inf = typeof(MyMathClass);

object[] attributes; attributes =

inf.GetCustomAttributes(typeof(AnyAttribute),false);

foreach(Object attribute in attributes) {

// read the attribute values }

Performing type discovery

You can use reflection to explore and examine the contents of an assembly. You can find the types associated with a module; the methods, fields, properties, and events associated with a type, as well as the signatures of each of the type's methods; the interfaces supported by the type; and the type's base class.

// what is in the assembly

Assembly a = Assembly.Load("Mscorlib.dll"); Type[] types = a.GetTypes( );

foreach(Type t in types) {

MessageBox.Show("Type is {0}" + t.ToString()); }

Late binding to methods and properties

You can reflect on a single type in the mscorlib assembly as well. To do so, you extract a type from the assembly with the GetType( ) method:

//examine a single object

Type theType = Type.GetType("System.Reflection.Assembly"); MessageBox.Show("Single Type is " + theType.ToString());

Also you can query the Assembly type for all its members using the GetMembers( ) method of the Type class, which lists all the methods, properties, and fields. Once you have discovered a method, it's possible to invoke it using reflection. You could, of course, call the method in the normal course of your code, but reflection allows you to bind to that method at runtime. This is called latebinding and offers the flexibility of choosing at runtime which object you will bind to and invoking it programmatically.

Creating types at runtime

One of the powerful use of reflection, however, is with reflection emit. Reflection emit supports the dynamic creation of new types at runtime. In the following sample a type is created on the fly; compiled and finally the type is loaded to memory.

// generate the code and compile it

private void GenerateCode(ref Type theType, ref object theClass) {

// open the file for writing string fileName = "EmitClass"; Stream s =

File.Open(fileName + ".cs", FileMode.Create); StreamWriter wrtr = new StreamWriter(s);

wrtr.WriteLine(

"// Dynamically created class"); // create the class

string className = "EmitClass";

wrtr.WriteLine("class {0}", className); wrtr.WriteLine("{");

// create the method

wrtr.WriteLine("\tpublic string Hello( )"); wrtr.WriteLine("\t{");

// write the brute force additions

wrtr.Write("\treturn \"Hello Reflection\"");

wrtr.WriteLine(";"); // finish method wrtr.WriteLine("\t}"); // end method wrtr.WriteLine("}"); // end class

// close the writer and the stream

wrtr.Close( );

s.Close( );

// Build the file ProcessStartInfo psi =

new ProcessStartInfo( );

psi.FileName = "cmd.exe";

string compileString = "/c csc /optimize+ "; compileString += "/target:library ";

compileString += "{0}.cs > compile.out";

psi.Arguments =

String.Format(compileString, fileName);

psi.WindowStyle = ProcessWindowStyle.Minimized; Process proc = Process.Start(psi);

proc.WaitForExit( ); // wait at most 2 seconds // Open the file, and get a

// pointer to the method info Assembly a = Assembly.LoadFrom(fileName + ".dll"); theClass = a.CreateInstance(className); theType = a.GetType(className); }

//reflect the type and invoke the method GenerateCode(theType, theClass);

// with the reference to the dynamically // created class you can invoke the method object[] arguments = new object[0];

object retVal =

theType.InvokeMember("Hello", BindingFlags.Default | BindingFlags.InvokeMethod, null, theClass, arguments);

Summary

‰ Reflection is the mechanism of discovering class information solely at run time. ‰ Using Reflection, you can obtain class and type information, member information.

Test Your Understanding

1. What are the uses of Reflection? 2. What is Reflection?

In document CSharp Handout v1.0 (Page 166-170)