Click here to Skip to main content
15,867,308 members
Articles / Programming Languages / C#
Tip/Trick

Compiling C# Code at Runtime

Rate me:
Please Sign up or sign in to vote.
4.96/5 (54 votes)
25 Jan 2014CPOL3 min read 240.5K   6.1K   83   24
This tip shows how to compile and use C# code during runtime and its impact on speed.

Introduction

Sometimes, it is very useful to compile code at runtime. Personally, I use this feature mostly in these two cases:

  • Simple web tutorials – writing a small piece of code into TextBox control and its execution instead of the necessity to own or to run some IDE.
  • User-defined functions – I have written an application for symbolic regression with simple configuration file where user can choose some of my predefined functions (sin, cos, etc.). The user can also simply write his own mathematical expression with basic knowledge of C# language.

If you want to use this feature, you don’t have to install any third-party libraries. All functionality is provided by the .NET Framework in Microsoft.CSharp and System.CodeDom.Compiler namespaces.

Hello World Program

For compiling code at runtime, you need to follow these few steps:

  1. Write your code – most important part. Here you can write classes and methods into the string variable. We will add only Main method:
    C#
    string code = @"
        using System;
    
        namespace First
        {
            public class Program
            {
                public static void Main()
                {
                " +
                    "Console.WriteLine(\"Hello, world!\");"
                    + @"
                }
            }
        }
    ";
  2. Create the provider and parameters of the compiler:
    C#
    CSharpCodeProvider provider = new CSharpCodeProvider();
    CompilerParameters parameters = new CompilerParameters();
  3. Define parameters of the compiler (optional) – at this point, we can add a reference to external libraries. We can also define whether our compiled code will be generated only in the memory or into the DLL or EXE file:
    C#
    // Reference to System.Drawing library
    parameters.ReferencedAssemblies.Add("System.Drawing.dll");
    // True - memory generation, false - external file generation
    parameters.GenerateInMemory = true;
    // True - exe file generation, false - dll file generation
    parameters.GenerateExecutable = true;
  4. Compile assembly:
    C#
    CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
  5. Check errors:
    C#
    if (results.Errors.HasErrors)
    {
        StringBuilder sb = new StringBuilder();
    
        foreach (CompilerError error in results.Errors)
        {
            sb.AppendLine(String.Format("Error ({0}): {1}", error.ErrorNumber, error.ErrorText));
        }
    
        throw new InvalidOperationException(sb.ToString());
    }
  6. Get assembly, type and the Main method:
    C#
    Assembly assembly = results.CompiledAssembly;
    Type program = assembly.GetType("First.Program");
    MethodInfo main = program.GetMethod("Main");
  7. Run it:
    C#
    main.Invoke(null, null);

And that’s it. We have run our first runtime-compiled program!

User-defined Functions

As I mentioned in the Introduction, I use this feature for runtime defining mathematical expressions. Let’s look at creating simple functions with two parameters.

This is very similar to the previous example (it’s assumed that parameters of the function are named x and y). In this code, we simply replace part of the string with our desired function and compile it:

C#
public static MethodInfo CreateFunction(string function)
{
    string code = @"
        using System;
            
        namespace UserFunctions
        {                
            public class BinaryFunction
            {                
                public static double Function(double x, double y)
                {
                    return func_xy;
                }
            }
        }
    ";

    string finalCode = code.Replace("func_xy", function);

    CSharpCodeProvider provider = new CSharpCodeProvider();
    CompilerResults results = provider.CompileAssemblyFromSource(new CompilerParameters(), finalCode);

    Type binaryFunction = results.CompiledAssembly.GetType("UserFunctions.BinaryFunction");
    return binaryFunction.GetMethod("Function");
}

At this point, we have written code for compiling user-defined functions so we use it by creating some function and invoking it:

C#
MethodInfo function = CreateFunction("x + 2 * y");
object result = function.Invoke(null, new object[] { 2, 3 });
Console.WriteLine(result);

In this case, the result is equal to 8 so our compiler works fine but we have got result of the Object type and we must also provide parameters of the Object type. There is a much better way – creating a delegate:

C#
var betterFunction = (Func<double, double, double>)Delegate.CreateDelegate
(typeof(Func<double, double, double>), function);

And there is a very simple way to invoke it:

C#
double result = betterFunction(2, 3);
Console.WriteLine(result);

Speed Comparison

We have written a simple function compiler so now we have one function of 4 types:

  1. Original, at compile-time-defined function
  2. Runtime-compiled function invoked by Reflection
  3. Delegate created from the runtime-compiled function
  4. Lambda Expression Delegate ((x, y) => x + 2 * y)

Let’s write a simple program to compare their speed:

C#
DateTime start;
DateTime stop;
double result;
int repetitions = 5000000;

start = DateTime.Now;
for (int i = 0; i < repetitions; i++)
{
    result = OriginalFunction(2, 3);
}
stop = DateTime.Now;
Console.WriteLine("Original - time: {0} ms", (stop - start).TotalMilliseconds);

start = DateTime.Now;
for (int i = 0; i < repetitions; i++)
{
    result = (double)function.Invoke(null, new object[] { 2, 3 });
}
stop = DateTime.Now;
Console.WriteLine("Reflection - time: {0} ms", (stop - start).TotalMilliseconds);

start = DateTime.Now;
for (int i = 0; i < repetitions; i++)
{
    result = betterFunction(2, 3);
}
stop = DateTime.Now;
Console.WriteLine("Delegate - time: {0} ms", (stop - start).TotalMilliseconds); 

start = DateTime.Now;
for (int i = 0; i < repetitions; i++)
{
    result = lambda(2, 3);
}
stop = DateTime.Now;
Console.WriteLine("Lambda - time: {0} ms", (stop - start).TotalMilliseconds);

After several tests, we will get the following results:

  1. Original – time: 92 ms
  2. Reflection – time: 3686 ms
  3. Delegate – time: 64 ms
  4. Lambda – time – 90 ms

Conclusion

Compiling C# code at runtime is very useful and easy task for various applications. I have shown how to create your own simple compiler. From speed results, it is obvious that speed of the runtime-compiled code is equal to the classical code (except the reflection-invoked case). 

License

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


Written By
Software Developer
Czech Republic Czech Republic
IT Specialist at Hyundai Glovis and Ph.D. student at Technical University of Ostrava, interested in C# language, .NET technology and CUDA platform.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Member 87648694-Apr-21 12:57
Member 87648694-Apr-21 12:57 
Questionusing the compiler Pin
shiraNadjari12-Nov-18 4:04
shiraNadjari12-Nov-18 4:04 
Question; expected Pin
özgür cinkılıç4-Jun-17 6:02
özgür cinkılıç4-Jun-17 6:02 
QuestionDoes it support changed files? Pin
jingjingtr15-Jun-16 22:55
jingjingtr15-Jun-16 22:55 
Questionobtaining value of variable instead of return value of a function Pin
r.kh22-Aug-15 20:02
r.kh22-Aug-15 20:02 
QuestionMy vote of 5 Pin
Lokukarawita21-Aug-15 2:13
Lokukarawita21-Aug-15 2:13 
QuestionGreate Pin
Aladár Horváth16-Aug-15 22:27
professionalAladár Horváth16-Aug-15 22:27 
QuestionKeeping a .exe file Pin
Adam Katav14-Jul-15 2:05
Adam Katav14-Jul-15 2:05 
QuestionHow to call back from a generated code? Pin
Michael Rusakow26-Apr-15 1:27
Michael Rusakow26-Apr-15 1:27 
SuggestionPerformance Measurement (It was mine) Pin
GGondim25-Mar-15 4:43
GGondim25-Mar-15 4:43 
SuggestionPerformance Measurement Pin
Member 1112623425-Mar-15 4:35
Member 1112623425-Mar-15 4:35 
Questionvery helpful to my project Pin
wjchicago8-Mar-15 21:22
wjchicago8-Mar-15 21:22 
GeneralMy vote of 5 Pin
Antonio Nakić Alfirević18-Nov-14 23:22
Antonio Nakić Alfirević18-Nov-14 23:22 
GeneralMy vote of 5 Pin
User 1106097918-Nov-14 1:00
User 1106097918-Nov-14 1:00 
QuestionYour performance testing condition for four-methods is not fair. Pin
Seong-Tae Jeong12-Feb-14 14:09
Seong-Tae Jeong12-Feb-14 14:09 
SuggestionPerformance measurement Pin
Member 1005484827-Jan-14 23:04
Member 1005484827-Jan-14 23:04 
Questionhow to save the result? Pin
MB Seifollahi27-Jan-14 1:47
professionalMB Seifollahi27-Jan-14 1:47 
AnswerRe: how to save the result? Pin
Lumír Kojecký27-Jan-14 2:06
professionalLumír Kojecký27-Jan-14 2:06 
GeneralRe: how to save the result? Pin
MB Seifollahi27-Jan-14 2:12
professionalMB Seifollahi27-Jan-14 2:12 
GeneralRe: how to save the result? Pin
LonelyCode31-Mar-14 3:18
LonelyCode31-Mar-14 3:18 
GeneralRe: how to save the result? Pin
Member 108419272-Mar-15 14:53
Member 108419272-Mar-15 14:53 
Questionmy vot of +5 Pin
abbaspirmoradi26-Jan-14 4:13
professionalabbaspirmoradi26-Jan-14 4:13 
I like this one. nice!
GeneralMy vote of 5 Pin
Oleg Shilo25-Jan-14 14:34
Oleg Shilo25-Jan-14 14:34 

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

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