Search code examples
c#compilationruntime

Compile a C# Array at runtime and use it in code?


I know C# code can be compiled at runtime using C#. However I'm very very shaky at it since I just read about it a few minutes ago. I learn a lot better by examples. So tell me. If I want to compile something like:

// MapScript.CS
String[] LevelMap = {
"WWWWWWWWWWWWWWWWWWW",
"WGGGGGGGGGGGGGGGGGW",
"WGGGGGGGGGGGGGGGGGW",
"WWWWWWWWWWWWWWWWWWW" };

and use this array in my code, how would I go about it?

In pseudocode I want to do something like this:

Open("MapScript.CS");
String[] levelMap = CompileArray("levelMap");
// use the array

Solution

  • LINQ Expression trees are probably the friendliest way of doing this: Perhaps something like:

    You can also generate the IL using OpCodes (OpCodes.Newarr). Easy if you are comfortable with stack-based programming (otherwise, can be challenging).

    Lastly, you can use the CodeDom (which your pseudocode resembles), but--while the most powerful tool--it is less ideal for quick dynamic methods. It requires file system permissions and manual reference resolution since you are working closely with the compiler.

    Sample from MSDN

    var ca1 = new CodeArrayCreateExpression("System.Int32", 10);                        
    var cv1 = new CodeVariableDeclarationStatement("System.Int32[]", "x", ca1);
    

    Source - Creating Arrays with the Code DOM

    If you want a straight up raw compile of a string, you can omit the object-oriented treatment of the statements and instead just build a big string. Something like:

    var csc = new CSharpCodeProvider( new Dictionary<string, string>() { { "CompilerVersion", "v4.0" } } );
    var cp = new CompilerParameters() {
        GenerateExecutable = false,
        OutputAssembly = outputAssemblyName,
        GenerateInMemory = true
    };
    
    cp.ReferencedAssemblies.Add( "mscorlib.dll" );
    cp.ReferencedAssemblies.Add( "System.dll" );
    cp.ReferencedAssemblies.Add( "System.Core.dll" );
    
    StringBuilder sb = new StringBuilder();
    
    // The string can contain any valid c# code, but remember to resolve your references
    
    sb.Append( "namespace Foo{" );
    sb.Append( "using System;" );
    sb.Append( "public static class MyClass{");
    
    // your specific scenario
    sb.Append( @"public static readonly string[] LevelMap = {
        ""WWWWWWWWWWWWWWWWWWW"",
        ""WGGGGGGGGGGGGGGGGGW"",
        ""WGGGGGGGGGGGGGGGGGW"",
        ""WWWWWWWWWWWWWWWWWWW"" };" );
    
    sb.Append( "}}" );
    
    // "results" will usually contain very detailed error messages
    var results = csc.CompileAssemblyFromSource( cp, sb.ToString() );