Click here to Skip to main content
Click here to Skip to main content

Dynamic Code Integration with CodeDom

, 23 May 2008 CPOL
Rate this:
Please Sign up or sign in to vote.
While there are many expression evaluators out there, the CodeDom framework allows you to take any .NET langauge and link a code snippet at run-time.

Introduction

I occasionally implement a program where I wish I could either change some parameters interactively or re-write a portion of code interactively. The parameters are easily solved with existing GUI components. The .NET framework, with the System.CodeDom namespace, provides a nice suite of tools for the latter. Several tutorials on using the CodeDom document exist, with most of them focusing on creating a source code file. In .NET 2.0, support was added to compile code from an internal string. This article will go over how to take a section of code from, say, a text box, and immediately use it within your system. I will also go over a few simple use cases that allow you to take a single expression or calculation from the user and embed that into a delegate function or your own interface hidden from the user. In part II, I will provide some robustness issues, provide additional examples, and discuss the utility of this with respect to the new LINQ technology. Note, I will use C# to explain this, but the concept will work equally well with any .NET language.

Initial Set-up

For this part of the tutorial, we will focus on a simple task: Outputting a string to the console. Let us start with this simple program, and see how we can refactor it to allow for more flexibility. The article is long, but mainly because I am going to take baby steps and repeat sections of the code as they are refactored. I am also going to ignore any error checking or exceptions until the end of the tutorial. Consider this very simple console application:

class Foo 
{
   public void Print()
   {
      System.Console.WriteLine("Hello from class Foo");
   }
}

static void Main( string[] args )
{
   Foo myFoo = new Foo();

   myFoo.Print();
}

Dynamic Compilation

My end goal is to take the source code in as a text string. So, as a first test, I convert the code above to a string, and then in Main, I will create an instance of Foo using the CodeDom technology and Reflection. This requires three steps:

  • Create an instance of an object that supports the ICodeCompiler interface. For our purposes, we want an instance which deals with C#, so we will use the CSharpCodeProvider in the Microsoft.CSharp namespace.
  • Compile the source code into an Assembly.
  • Use Reflection on the Assembly to get information on the types, and create an instance of a type.

To aid in this, I will create a utility method called CompileSource that will take in a string and output an Assembly. Here is the code for this:

private static Assembly CompileSource( string sourceCode )
{
   CodeDomProvider cpd = new CSharpCodeProvider();
   CompilerParameters cp = new CompilerParameters();
   cp.ReferencedAssemblies.Add("System.dll");
   cp.ReferencedAssemblies.Add("ClassLibrary1.dll");
   cp.GenerateExecutable = false;
   // Invoke compilation.
   CompilerResults cr = cpd.CompileAssemblyFromSource(cp, sourceCode);

   return cr.CompiledAssembly;
}

Fairly straightforward; in addition to creating the ICodeCompiler and compiling the source, we needed to create a CompilerParameters instance and configure it. Note that you can add any reference that you wish to expose to your users. The resulting Assembly is extracted from an instance of the CompilerResults class that the CompileAssemblyFromSource method returns. Okay, now let’s put this to use. Below is the new application (minus the CompileSource code above). I am using the string @ operator to specify the source code. With this syntax, any quotation marks in the string need to be repeated:

namespace CreateDelegate
{
   class Program
   {
      static void Main( string[] args )
      {
          Test1();
      }
      private static void Test1()
      {
         //
         // Create an instance of type Foo and call Print
         //
         string FooSource = @"
            class Foo
            {
               public void Print()
               {
                  System.Console.WriteLine(""Hello from class Foo"");
               }
            }";

         Assembly assembly = CompileSource(FooSource);
         object myFoo = assembly.CreateInstance("Foo");
         // myFoo.Print(); // - Print not a member of System.Object
         // ((Foo)myFoo).Print(); // - Type Foo unknown
      }
   }
}

Note, Main has successfully created an instance of the Foo class, but there are a few problems:

  1. Main has knowledge of the class name (“Foo”) hard-coded within it.
  2. When you compile this application, the type Foo does not exist. It is created at run-time. As such, we have to use the unified type object to deal with our instance of Foo. There is no way to cast it to the type Foo.
  3. As a result of 2 above, I can not call Print using myFoo.Print. That will produce a compiler error, as the type object does not have a method Print.

Hence, the new version does not print anything. Using Reflection, it is possible to get the list of methods for the class Foo and to Invoke the Print method directly. Doing so directly is rather clumsy, and not a very useful programming practice. Instead, I will cover two approaches to deal with these types, that provide a more elegant solution:

  • Programming to Interfaces
  • Using Delegates

Both of these require additional knowledge in either the main program, the source code string, or both. Which one to use depends on your application’s particular needs, and will be covered in the use cases later.

Programming to Interfaces

A fundamental tenant of modern software design is to program to interfaces. For our simple example, we will add an IPrint interface:

namespace FooLibrary
{
   public interface IPrint
  {
      void Print();
   }
}

I have placed this in the namespace FooLibrary. Having an interface is not sufficient. It needs to be accessible to any class that wishes to implement it. The best way to accomplish this is to place your key interfaces into one or more type libraries (DLLs). I have created the DLL IPrint.dll for this project. I have also added it as a reference to our console application. The modified test is as follows:

using FooLibrary;
private static void Test2()
{
   //
   // Create an instance of type FooLibrary.IPrint and call Print
   //
   string FooInterface = @"
      class Foo : FooLibrary.IPrint {
         public void Print() {
            System.Console.WriteLine(""Hello from class Foo"");
         }
      }";

   Assembly assembly = CompileSource(FooInterface);
   IPrint foo = assembly.CreateInstance("Foo") as IPrint
   if (foo != null)
   {
      foo.Print();
   }
}

We have made the following changes:

  1. We added a using statement for FooLibrary.
  2. We cast the result of CreateInstance to the type IPrint (using the as operator).
  3. If the cast is successful, we can now call Print().

In order for our string-based source code to compile, it needs access to the type IPrint in the IPrint.dll assembly. Our CompileSource method needs the following line added:

cp.ReferencedAssemblies.Add("IPrint.dll");

Note that even though we include a using statement for FooLibrary, we still need to fully qualify IPrint within the string fooSource. This makes sense, since it is treated as a separate compile unit. We could have also added an additional using FooLibrary inside the string.

Searching for Types Using Reflection

This solves two of the three problems mentioned above. Main still has a hard-coded reference to the string name “Foo”. Having the name of the class hard-wired can easily be replaced with an additional user control, letting the user specify the name of the type to create. This would be error prone as the user changes the code, but perhaps forgets to change the name. To remove the implementation intelligence of knowing the concrete type name from Main, we can search the assembly. What we want is to take an assembly and a well-known interface name, and search for the concrete type name that implements the interface. This is accomplished as follows:

private static void Test2()
{
   //
   // Create an instance of type FooLibrary.IPrint and call Print
   //
   string FooInterface = @"
      class Foo : FooLibrary.IPrint {
         public void Print() {
            System.Console.WriteLine(""Hello from class Foo"");
         }
      }";

   Assembly assembly = CompileSource(FooInterface);

   Type fType = assembly.GetTypes()[0];
   Type iType = fType.GetInterface("FooLibrary.IPrint");
   if (iType != null)
   {
      FooLibrary.IPrint foo = (FooLibrary.IPrint)
            assembly.CreateInstance(fType.FullName);
      foo.Print();
   }
}

Here, we made the assumption that there is only one type, or the type we are interested in is the first one. This can easily be changed to search through all types, and is left as an exercise to the reader. So, we can now create instances of a type supporting our interface, where the type definition starts out initially as a text string. If the end goal is simply to execute the statements within the Print() method, then a simpler scheme can be constructed using delegates, which we will talk about next.

Dynamic Delegates

Let’s consider the case where we wish to allow the user to specify an arbitrary two-dimensional function, f(x,y), using some user-interface component. Here is an example:

float func2D( float x, float y)
{
   float value; value = Math.Cos(x) * Math.Sin(x) + 1.0f;
   return value;
}

There are many articles about creating your own expression engine and parsing a function like this one. With CodeDom, you can treat any .NET language as your scripting language. How is this function different from our Print method above? Well, for one, it is not contained in a class or a namespace. You can use Reflection to access this function from the global namespace, but I am going to take a different route. I am going to wrap it in a class so we can use a similar logic to the above. I structured this code very specifically. The input I want from the user is simply the line (or set of statements):

value = Math.Cos(x) * Math.Sin(y) + 1.0f;

This will alleviate the user from knowing the exact function signature, the class name, interface name, etc. How? Through string manipulations! I define two additional strings, called methodHeader and methodFooter. I then take the string for the expression, and sandwich it between the header and footer strings before passing it to the CompileSource method. For example, the original example can be coded like so:

string FooSourcHeader = @"
   using System;
   class Foo {
      static public void Print()
      {";
string FooSourceFooter = @"
      }
   }";

string myPrint = FooSourcHeader
               + @"Console.WriteLine(""Embedded Hello"");"
               + FooSourceFooter;

Assembly fooAssembly = CompileSource(myPrint);
IPrint myFoo = fooAssembly.CreateInstance(“Foo”) as IPrint;
myFoo.Print();

What is the advantage of doing this? Well, now I know that “Foo” is the class name and that it implements the IPrint interface. I have control over those strings! The user only needs to know that they are to provide a sequence of zero or more statements. I can even provide a more detailed class definition and tell the users which fields they have access to. Okay, so that is cool, but this section is about dynamic delegates. A delegate defines a type for a method signature. They are commonly used in callbacks and with events. Any method with the same signature can be treated as the delegate’s type.

        delegate void PrintDelegate();
        private static void Test3()
        {
            //
            // Get a PrintDelegate from an instance of the type Foo.
            //
            string FooSourcHeader = @"
               using System;
               class Foo {
                   static public void Print()
                   {";
            string FooSourceFooter = @"
                   }
               }";

            string myPrint = FooSourcHeader
                + @"Console.WriteLine(""Hello from Print delegate"");"
                + FooSourceFooter;

            Assembly assembly = CompileSource(myPrint);
            //
            // Try to Invoke a method
            //
            Type fooType = assembly.GetType("Foo");
            MethodInfo printMethod = fooType.GetMethod("Print");
            PrintDelegate fooPrint = (PrintDelegate)
                  Delegate.CreateDelegate(typeof(PrintDelegate), printMethod);
            fooPrint();
        }

Summary

There you have it. We can use text strings to add functionality to our system at run-time. We can allow full class definitions if we want, or simple method bodies. In part II, I will provide some user controls and example demos built with these ideas. I will also go over some robustness issues, and how to present any compiler errors back to the user.

History

  • 05-22-2008 - Initial article.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Crawfis
Instructor / Trainer The Ohio State University
United States United States
No Biography provided

Comments and Discussions

 
GeneralShare an object or a variable from the main program Pinmemberfaludigabor15-Jun-08 10:52 
GeneralRe: Share an object or a variable from the main program PinmemberCrawfis10-Jul-08 9:29 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141216.1 | Last Updated 23 May 2008
Article Copyright 2008 by Crawfis
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid