Chapter 5. Reflection and Dynamic Programming

The principles of Reflection in computer science are defined by Wikipedia as:

The ability of a computer program to examine, introspect, and modify its own structure and behavior.

The internal structure of the .NET assemblies we saw in the first chapter allows us to load and invoke types embedded inside our own or foreign assemblies at runtime with a technique called dynamic invocation.

Moreover, classes related to CodeDOM and Reflection.Emit namespaces permit code generation at runtime, either in C# or other languages, including Intermediate Language (IL).

However, beyond the .NET-to-NET dialogs, we can use Interoperability to manipulate applications built in other non-NET programming languages. Actually, many professional applications find it suitable—and very useful—to count on external functionalities that we might detect as present in the host operating system. This means that we can interoperate with Microsoft Office Suite (Word and Excel being the most typical cases of these resources).

These applications can provide us with new and exciting possibilities, such as graph (charts) generation, text spelling, document template creation, and even add-in enhancements.

That's why our goal in this chapter is to review some of the most useful concepts and use cases that a programmer might find of interest in relation with these topics.

We will start with Reflection, analyzing the possibilities offered by .NET Framework to introspect the very structure of assemblies and invoke internal functionalities in a totally programmable way.

I will also cover the ability of emitting source code at runtime and generate new types and launch them at runtime.

In the second part, we will review the most noticeable and statistically used options offered by Interop programming, the name used to indicate when a program communicates with another application to establish a controlled, programmatic dialog in order to interchange data and emit instructions to other applications.

So, in brief, we will go through the following topics:

  • Concepts and the implementation of Reflection in the .NET framework
  • Typical uses of Reflection in everyday programming
  • Using System.Emit to generate source code at runtime
  • Interop programming from the C# language
  • Using Interop with Microsoft Office apps
  • Creating Office add-ins or apps

Reflection in the .NET Framework

As always, it is good to start with the main definition (MSDN source), which states that:

The classes in the System.Reflection namespace, together with System.Type, enable you to obtain information about loaded assemblies and the types defined within them, such as classes, interfaces, and value types. You can also use Reflection to create type instances at run time, and to invoke and access them.

Remember, as we mentioned in the first chapter, that the organization of assemblies is such that they contain modules, which in turn contain types that contain members. Reflection techniques allow you to find out (introspect) which modules, types, and members are present inside a given assembly.

Therefore, when we access any member via Interop, there's a hierarchy of info properties linked to it: the generic member's info, its System.Type (the type it belongs to) namespace, the method base, and also information related to its properties, fields and events, as the next graphic shows:

Reflection in the .NET Framework

As per the .NET architecture analyzed in previous chapters, the two core elements that enable this behavior are the assembly's metadata and the .NET's dynamic common type system.

A simple look at the basic members of the highest element in .NET hierarchy (System.Object) shows that Reflection is at the very core of its inception, since we have a GetType() method that is going to be present all along the chain of objects.

Actually, GetType() returns an instance of the System.Type class, served in a way that encapsulates all the metadata of the object being examined. It is with this System.Type instance that you will be able to traverse all details of the type or class (except the IL code) and also gain the ability to discover the surrounding context: the module that implements that type and its containing assembly.

In the Chapter 3, Advanced Concepts of C# and .NET, we included the following sample to test the very basics of Reflection:

dynamic dyn = "This is a dynamic declared string";
Type t = dyn.GetType();
PropertyInfo prop = t.GetProperty("Length");
int stringLength = prop.GetValue(dyn, new object[] { });
Console.WriteLine(dyn);
Console.WriteLine(stringLength);

In this code, we're using GetType() and casting the result to a Type object, which we can later use to inspect the members of the dyn variable. A look at the Object Browser in the search for the System.Type instance makes things quite clear:

Reflection in the .NET Framework

The screenshot shows how System.Type implements the IReflect interface, which provides a gang of methods for introspection (most of them starting with Get, followed by the target introspection to find out fields, members, and so on).

Also, note the presence of the InvokeMember method, which permits dynamic invocation of the type's members at runtime and can be used for a variety of purposes. The return values of these methods are arrays that represent the information structure of every individual member: MemberInfo[], PropertyInfo[], MethodInfo[], and FieldInfo[].

Now, let's start coding some of these ideas in a simple console application that declares a Person class with three properties and a method and learn how we get all this information at runtime.

Please, notice that the Person class owns a property which uses a method declared in a distinct namespace (System.Windows.Forms). There's no problem to access that method via Reflection and invoking it, only that we have to reference the namespace, together with System.Reflection, that we'll use a bit later:

using System;
using System.Reflection;
using System.Windows.Forms;
using static System.Console;

namespace Reflection1
{
  class Program
  {
    static void Main(string[] args)
    {
      Person p = new Person()
      {
        eMail = "person@email",
        Name = "Person Name",
        BirthDate = DateTime.Today
      };
      WriteLine($"Type of p: { p.GetType() }");
      Read();
    }
  }
  class Person
  {
    public string Name { get; set; }
    public string eMail { get; set; }
    public DateTime BirthDate { get; set; }
    public void ShowPersonData(string caption, MessageBoxIcon icon)
    {
      MessageBox.Show(this.Name + " - " + this.BirthDate,
      caption, MessageBoxButtons.OK, icon);
    }
  }
}
// Output: "Type of p:  Reflection1.Person"

We're including the output in the code since it is pretty predictable, and we're just asking for the type. However, let's continue adding some more lines before the call to Read(), to find out more about the Person class:

WriteLine($"Type of p: { p.GetType() }");
Type tPerson = p.GetType();
WriteLine($"Assembly Name: {tPerson.Assembly.ToString()}");
WriteLine($"Module Name (Path removed): {tPerson.Module.Name}");
WriteLine($"Name of the undelying type: {tPerson.UnderlyingSystemType.Name}");
WriteLine($"Number of Properties (public): {tPerson.GetProperties().Length}");
// Now ler's retrieve all public members
var members = tPerson.GetMembers();
foreach (var member in members)
{
  WriteLine($"Member: {member.Name}, {member.MemberType.ToString()}");
}
Read();

Now, we find out something else about the internal structure, as shown in the output:

Reflection in the .NET Framework

Some of the hidden members show up now, such as the default constructor created by the compiler (.ctor), the conversion of the {get; set;} declarations into pairs of field/access methods, and the inherited members from the object. Using these methods, we obtain all the information that's relative to a member.

Not only can we find out the structure of another type, but we can also invoke its members, as mentioned earlier. For example, the ShowPersonData method receives two parameters to configure a MessageBox object, where it presents some information to the user.

This means that we need to be able to call the method and also configure and send the parameters it requires. We can do this with the following code:

// Invoke a method
var method = tPerson.GetMethod("ShowPersonData");
object[] parameters = new object[method.GetParameters().Length];
parameters[0] = "Caption for the MessageBox";
parameters[1] = MessageBoxIcon.Exclamation;
method.Invoke(p, parameters);

Since parameters can be of any type, we create an object array that will be used at runtime to correspond every item in the array with its argument in the method. In this case, we want to pass the caption of the dialog box and the icon to be used.

As expected, we get the corresponding MessageBox object at runtime with the correct configuration:

Reflection in the .NET Framework

Of course, we can also perform the manipulation of properties in a similar manner:

// Change a Property
WriteLine(" Write/Read operations
");
var property = tPerson.GetProperty("Name");
object[] argums = { "John Doe" };
WriteLine($" Property {property.Name} - Is writable: {property.CanWrite}");
tPerson.InvokeMember("Name", BindingFlags.SetProperty, null, p, argums);
WriteLine($" Property {property.Name}: written ok.");
// Read the Name property
object value = tPerson.InvokeMember(property.Name, BindingFlags.GetProperty, null, p, null);
WriteLine($" New {property.Name} is: {value}");

The output confirms that the property was of a read/write kind and also confirms the results of the change (note that we don't pass any argument to read the data):

Reflection in the .NET Framework

Calling external assemblies

If we need information and/or functionality concerning a different assembly, that's also possible using the Assembly object or by referencing the assembly and obtaining its data from the static GetType() method of the Type object.

This includes those that are part of .NET Framework itself. To access all these functionalities, the System.Reflection namespace provides a cluster of related possibilities. A syntax such as this can serve the purpose:

using System;
using System.Windows.Forms;
using System.Reflection;
using static System.Console;

namespace Reflection1
{
  class Program2
  {
    static void Main(string[] args)
    {
      // Direct reflection of a referenced type
      // (System.Math belongs to mscorlib)
      WriteLine("
 MemberInfo from System.Math");
      // Type and MemberInfo data.
      Type typeMath = Type.GetType("System.Math");
      MemberInfo[] mathMemberInfo = typeMath.GetMembers();
      // Shows the DeclaringType method.
      WriteLine($"
 The type {typeMath.FullName} contains {mathMemberInfo.Length} members.");
      Read();
    }
  }
}

// output:
// MemberInfo from System.Math
// The type System.Math contains 76 members.

So, we're reflecting on a reference type (System.Math) included in the basic library, mscorlib, to find out how many members are included, such as in the previous case.

Alternatively, we can load an assembly at runtime using the Assembly object and even create an instance of that object using the CreateInstance() static method with code like this:

// Loading an assembly at runtime.
Assembly asm = Assembly.Load("mscorlib");
Type ty = asm.GetType("System.Int32");
WriteLine(ty.FullName);
Object o = asm.CreateInstance("System.Int32");
WriteLine(o.GetType().FullName);   // => System.Int32

It is also possible to get all the referenced assemblies of the current (running) assembly. The GetExecutingAssembly() method returns an Assembly object pointing to itself, and by calling GetReferencedAssemblies(), we get all this information. The following code will suffice to obtain this list:

// Get information on assemblies referenced in the current assembly.
AssemblyName[] refAssemblies;
refAssemblies =   Assembly.GetExecutingAssembly().GetReferencedAssemblies();
WriteLine(" Assemblies referenced by the running assembly:");
foreach (var item in refAssemblies)
{
  Console.WriteLine(" " + item.FullName);
}
Read();

The entire output (including the three previous routines) looks like what is shown in the following screenshot:

Calling external assemblies

Generic Reflection

Reflection of generic types is also available and can be checked using Boolean properties, such as IsGenericType, IsGenericTypeDefinition, or GetGenericArguments(). The same mechanisms apply in this case, only checking the corresponding types for the difference. The following is a short demo, which declares a generic Dictionary object and recovers information on its types:

using System;
using static System.Console;
using System.Collections.Generic;

namespace Reflection1
{
  class Program3
  {
    static void Main(string[] args)
    {
      var HttpVerbs = new Dictionary<string, string>();
      HttpVerbs.Add("Delete", "Requests that a specified URI be deleted.");
      HttpVerbs.Add("Get", "Retrieves info that is identified by the URI of the request");
      HttpVerbs.Add("Head", "Retrieves the message headers ");
      HttpVerbs.Add("Post", "Posts a new entity as an addition to a URI.");
      HttpVerbs.Add("Put", "Replaces an entity that is identified by a URI.");

      // Reflection on a generic type
      Type t = HttpVerbs.GetType();
      WriteLine($"
 {t}");
      WriteLine($" Is a generic type? {t.IsGenericType}");
      WriteLine($" Is a generic type definition? {t.IsGenericTypeDefinition}");

      // Info on type parameters or type arguments.
      Type[] typeParameters = t.GetGenericArguments();

      WriteLine($" Found {typeParameters.Length} type arguments:");
      foreach (Type tParam in typeParameters)
      {
        if (tParam.IsGenericParameter)
        {
          // Display Generic Parameters (if any);
          Console.WriteLine($" Type parameter: {tParam.Name} in position: " + $" {tParam.GenericParameterPosition}");
        }
        else
        {
          Console.WriteLine($" Type argument: {tParam}" );
        }
      }
      Read();
    }
  }
}

The—quite predictable—output shows characteristics about the type (generic) and its members (non generic) and iterates over a loop, checking for the genericity of every type (using the IsGenericParameter Boolean property) before printing its details:

Generic Reflection

So, changing the methods' calls and/or adding some checking, we can also use generic types with Reflection, just as we did with the classical ones.

Emitting code at runtime

Another interesting possibility is the capacity of some classes in the .NET Framework to emit code at runtime and—eventually—compile and run it. Observe that Visual Studio itself creates code in many scenarios: when creating the structure of documents in different languages from a template, using code snippets, scaffolding an ORM in ASP.NET, and so on.

This is a task that can be achieved mainly in two ways: using CodeDOM or by means of classes inside the System.Reflection.Emit namespace.

The System.CodeDOM namespace

The first option refers to the System.CodeDOM and System.CodeDOM.Compiler namespaces, and it's been present in .NET since the very first version of the framework. Note the DOM part of the name: it means Document Object Model, just like in HTML, XML, or other document structures.

With classes inside CodeDOM, we can generate source code using templates that define coding structures in different languages, so we can even generate source code in all languages supported by .NET Framework.

To generate code for any .NET structure, CodeDOM classes represent any aspect of the generated code, and we should use two different mechanisms: one that expresses the elements to be built and another that—when lunched—produces the actual code.

Let's imagine how can we generate the previous Person class; only, reduce it to a minimum of elements for sake of clarity.

We will need the following:

  • An instance of CodeCompileUnit, which is in charge of generating the DOM structure of our component (its code graph). This structure is responsible for scaffolding the different members our class contains: properties, fields, methods, and so on.
  • The rest of the elements have to be created one by one using classes available in CodeDOM, which represent every possible reserved word or structure (classes, methods, parameters, and so on).
  • Finally, all elements created individually are included in the CodeCompileUnit object prior to its generation.

Let's take a look at the code, which produces a file that contains the same definition of a Person class that we used at the beginning of this chapter (for the sake of brevity, I'm just including here the initial lines and final lines. You'll find the whole code in demo Reflection1 inside this chapter's source code):

using System.CodeDom;
using System.Reflection;
using System.CodeDom.Compiler;
using System.IO;

namespace Reflection1
{
  class Program5
  {
    static void Main(string[] args)
    {
      CodeCompileUnit oCU = new CodeCompileUnit();
      CodeTypeDeclaration oTD;

      // Working with CodeDOM
      CodeNamespace oNamespace = new CodeNamespace("Reflection1");
      // CodeNameSpace can import declarations of other namespaces
      // which is equivalent to the "using" statement
      oNamespace.Imports.Add(new CodeNamespaceImport("System.Windows.Forms"));
      // Class creation is undertaken by CodeTypeDeclaration class.
      // You can configure it with attributes, properties, etc.
      oTD = new CodeTypeDeclaration();
      oTD.Name = "Person";
      oTD.IsClass = true;


      // Generate code
      CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
      CodeGeneratorOptions options = new CodeGeneratorOptions();
      options.BracingStyle = "C";
      using (StreamWriter sr = new StreamWriter(@"Person.cs"))
      {
        provider.GenerateCodeFromCompileUnit(oCU, sr, options);
      }
    }
  }
}

Once we establish our entry point in Program5, its execution generates a file with the same code that we had in the Program.cs file.

Tip

Note that you will have to check the bin/debug directory, since we didn't establish a different output path.

You should see something like the following screenshot in your Solution Explorer:

The System.CodeDOM namespace

As you can see, the generated code is pretty verbose. However, that's what it takes to generate code feature by feature. Let's go briefly through it and underline the most important classes implied in the generation, starting with the end of the Main method.

The object that puts everything to work is the CodeDomProvider class. It has to be instantiated, indicating the language to be used (CSharp, in our case). At the end, we will invoke its GenerateCodeFromCompileUnit method, which will use all previous definitions to produce the actual code inside the file defined for this purpose.

So, at the top of Program5, we declare a CompilerUnit object. The other crucial component is the CodeTypeDeclaration object, which is in charge of storing all declarations used while code generation takes place.

The rest of the classes implied in the construction are merely helper classes to build each brick of the resulting class. This is the role of the CodeNamespace, CodeNamespaceImport, CodeSnippetTypeMember, CodeCommentStatement, CodeMemberMethod, and CodeParameterDeclarationExpression classes.

Although you may think that this is too much effort to just create the resulting class, keep in mind that the automatization of tasks inside Visual Studio follows similar paths and that you could create you own mechanisms of code generation suited to your needs or the company's requirements.

With such programmable context, it's easy to imagine a number of situations in which code generation can be tuned by the generating program at runtime, allowing you to produce code with different variations depending on options we established previously.

The Reflection.Emit namespace

The System.Reflection.Emit namespace is intended for code generation, allowing developers to create code or metadata from within their applications, independent of the specifics of the operating system's loaders.

Basically, this namespace offers the following programmatic options:

  • It allows the building of modules and assemblies at runtime
  • It creates classes and types and emits IL
  • It launches a .NET compiler to build apps

The way the code is generated here is different from CodeDOM in several aspects. One of them is the ability to generate IL code at runtime, which means that the execution of some C# code will produce outputs not coded in C# but using these instruments.

One of the objects implied in this type of code generation is the DynamicMethod object included in the System.Reflection.Emit namespace. This object allows the obtention of another object of type ILGenerator.

Once you get ILGenerator, you can produce IL code dynamically in a very straightforward manner:

// 1st sample of emission (using ILGenerator):
// A way to obtain one is creating a dynamic method. When the
// method is invoked, its generated contents are executed.
DynamicMethod dn = new DynamicMethod("dynamicMethod", null, null);
var ilgen = dn.GetILGenerator();
ilgen.EmitWriteLine("Testing Reflection Emit.");
ilgen.EmitWriteLine("We use IlGenerator here...");
ilgen.Emit(OpCodes.Ret);
dn.Invoke(null, null);

Observe how we finally call the DynamicObject.Invoke method as if it were a delegate. If you test the previous code, the output generated will correspond to what is the equivalent of the counterpart lines of the code programmed in C#, which produces the same information in the output:

The Reflection.Emit namespace

Also, note the presence of the OpCodes.Ret value in the last call to ilgen.Emit. This generates a return statement, which pushes the return value, if present, from the evaluation stack to the caller's evaluation stack.

If you take a look at fields related to OpCodes, you'll discover that it provides an extensive list of field representations of MSIL instructions, as shown in the next screenshot:

The Reflection.Emit namespace

Tip

Note that if you want to take a deep dive into these possibilities, there's a page on MSDN, which contains an exhaustive relation of all the OpCodes at https://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes_fields(v=vs.110).aspx.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.154.252