During development time you add a reference to an assembly so it gets included with the assembly references and the types of the assembly are available to the compiler. During runtime the referenced assembly gets loaded as soon as a type of the assembly is instantiated or a method of the type is used. Instead of using this automatic behavior, you can also load assemblies programmatically. To load assemblies programmatically you can use the class Assembly with the static method Load ( ) . This method is overloaded where you can pass the name of the assembly using AssemblyName, the name of the assembly, or a byte array.
It is also possible to create an assembly on the fly as shown with the next example. This sample demonstrates how C# code can be entered in a text box, a new assembly is dynamically created by starting the C# compiler, and the compiled code is invoked.
To compile C# code dynamically you can use the class CSharpCodeProvider from the namespace Micr~soft . CSharp. Using this class, you can compile code and generate assemblies from a DOM tree, from a file, and from source code.
The UI of the application is done using WPF. You can see the UI. The window is made up of a TextBox to enter C# code, a Button, and a TextBlock WPF control that spans all columns of the last row to display the result as shown.
To dynamically compile and run C# code, the class CodeDriver defines the method CompileAndRun (). This method compiles the code from the text box and starts the generated method.
The method CompileAndRun () requires a string input parameter where one or multiple lines of C# code can Ere passed. Because every method that is called must be included in a method and a class, the variables prefix and postfix define the structure of the dynamically created class Driver and the method Run () that surround the code from the parameter. Using a StringBuilder, the prefix, ‘postfix, and the code from the input variable are merged to create a complete class that can be compiled. Using this resultant string, the code is compiled with the CSharpCodeProvider class. The method CompileAssemblyFroIt\Source () dynamically creates an assembly. Because this assembly is just needed in memory, the compiler parameter option GenerateInMemory is set.
H the source code that was passed contains some errors, these will show up in the Errors collection of CompilerResul ts. The errors are returned with the return data, and the variable has Error is set to true.
If the source code compiled successfully, the Run () method of the new Driver class is invoked. The invocation of this method is done using reflection. From the newly compiled assembly that can be accessed using CompilerResults. CompiledType, the new class Driver is referenced by the driverType variable. Then the InvokeMember () method of the Type class is used to invoke the method Run ( ) . Because this method is defined as a public static method; the BindingFlags must be set accordingly. To see a result of the program that is written to the console, the console is redirected to a StringWriter to finally return the complete output of the program with the returnData variable.
The Click event of the WPF button is connected to the Compile_Click () method where the CodeDri~er class is instantiated, and the CompileAndRun () method is invoked. The input is taken from the TextBox named t ext code, and the result is written to the Text Block textOutput.
Now you can start the application, enter C# code in the TextBox as shown, and compile and run the code.
The program as written so far has the disadvantage that every time you click the Compile and Run button, a new assembly is created and loaded, and the program always needs more and more memory. You cannot unload an assembly from the application. To unload assemblies, application domains are needed.