Introduction
The System.CodeDom namespace in .net allows the users to dynamically compile and create assemblies. This concept is used by ASP.NET internally. The article provides an insight on how to create assemblies dynamically.
The following are the list of classes, which would be used to create the assembly:
- CodeNamespace
- CodeTypeDeclaration
- CodeCompileUnit
- CodeEntryPointMethod
- CodeMemberField
- CodeMemberProperty
- CodeMemberMethod
- CodeParameterDeclarationExpression
- CodeCompileUnit
- CompilerParameters
- CodeSnippetExpression
- CodeExpressionStatement
Step 1
Add the following namespace declarations,
- using System;
- using System.CodeDom;
- using System.CodeDom.Compiler;
The System.Codedom namespace contains the classes/types necessary to create assemblies at runtime. The above-mentioned classes are contained within this namespace.
Step 2: Create the following class,
- public class CCodeGenerator {
- CodeNamespace mynamespace;
- CodeTypeDeclaration myclass;
- CodeCompileUnit myassembly;
- }
Any code created from Step 3 to Step 10 must reside in the CcodeGenerator class. For clarity and explanation sake, the article has fragmented the code into pieces.
Step 3
The CodeNamespace class is used to add/create namespaces. The name of the namespace can be passed through the constructor of the class.
- public void CreateNamespace()
- {
- mynamespace = new CodeNamespace("mynamespace");
- }
Step 4
Using the mynamespace object, add the list of all namespaces that are used by the assembly.
- public void CreateImports() {
- mynamespace.Imports.Add(new CodeNamespaceImport("System"));
- mynamespace.Imports.Add(new CodeNamespaceImport("System.Drawing"));
- mynamespace.Imports.Add(new CodeNamespaceImport("System.Windows.Forms"));
- }
Step 5
Create a class inside the namespace.
- public void CreateClass() {
- myclass = new CodeTypeDeclaration();
-
- myclass.Name = "CMyclass";
- myclass.IsClass = true;
-
- myclass.Attributes = MemberAttributes.Public;
-
- mynamespace.Types.Add(myclass);
- }
Step 6
Create a member and add it to the newly created class.
- public void CreateMember() {
-
- CodeMemberField mymemberfield = new CodeMemberField(typeof(System.String), "strMessage");
-
- myclass.Members.Add(mymemberfield);
- }
Step 7
Create a property (with get and set) and add it to the class
- public void CreateProperty() {
- CodeMemberProperty mymemberproperty = new CodeMemberProperty();
-
- mymemberproperty.Name = "Message";
-
- mymemberproperty.Type = new CodeTypeReference(typeof(System.String));
-
- mymemberproperty.Attributes = MemberAttributes.Public;
-
- myclass.Members.Add(mymemberproperty);
-
-
-
-
- CodeSnippetExpression getsnippet = new CodeSnippetExpression("return strMessage");
-
- CodeSnippetExpression setsnippet = new CodeSnippetExpression("strMessage=value");
-
- mymemberproperty.GetStatements.Add(getsnippet);
- mymemberproperty.SetStatements.Add(setsnippet);
- }
Step 8
Create a method. Please take utmost care when assigning values to the CodeSnippetExpression class, as any statement provided must not have any syntax error(s). Since the assembly would be created at runtime (dynamically), the compiler would simply throw all Compile-time error(s) and we don't have any prudent means to find and eliminate the error.
- public void CreateMethod() {
-
- CodeMemberMethod mymethod = new CodeMemberMethod();
-
- mymethod.Name = "AddNumbers";
-
- CodeParameterDeclarationExpression cpd1 = new CodeParameterDeclarationExpression(typeof(int), "a");
- CodeParameterDeclarationExpression cpd2 = new CodeParameterDeclarationExpression(typeof(int), "b");
-
- mymethod.Parameters.AddRange(new CodeParameterDeclarationExpression[] { cpd1, cpd2 });
-
- CodeTypeReference ctr = new CodeTypeReference(typeof(System.Int32));
-
- mymethod.ReturnType = ctr;
-
- CodeSnippetExpression snippet1 = new CodeSnippetExpression("System.Console.WriteLine(\" Adding :\" + a + \" And \" + b )");
-
- CodeSnippetExpression snippet2 = new CodeSnippetExpression("return a+b");
-
- CodeExpressionStatement stmt1 = new CodeExpressionStatement(snippet1);
- CodeExpressionStatement stmt2 = new CodeExpressionStatement(snippet2);
-
- mymethod.Statements.Add(stmt1);
- mymethod.Statements.Add(stmt2);
-
- mymethod.Attributes = MemberAttributes.Public;
-
- myclass.Members.Add(mymethod);
- }
Step 9
Create a main method (Entry point for the assembly);
- public void CreateEntryPoint() {
-
- CodeEntryPointMethod mymain = new CodeEntryPointMethod();
- mymain.Name = "Main";
-
- mymain.Attributes = MemberAttributes.Public | MemberAttributes.Static;
-
-
-
- CodeSnippetExpression exp1 = new CodeSnippetExpression("CMyclass x = new CMyclass()");
-
- CodeSnippetExpression exp2 = new CodeSnippetExpression("x.Message=\"Hello World \"");
-
- CodeSnippetExpression exp3 = new CodeSnippetExpression("Console.WriteLine(x.Message)");
-
- CodeSnippetExpression exp4 = new CodeSnippetExpression("Console.WriteLine(\"Answer: {0}\",x.AddNumbers(10,20))");
- CodeSnippetExpression exp5 = new CodeSnippetExpression("Console.ReadLine()");
-
- CodeExpressionStatement ces1 = new CodeExpressionStatement(exp1);
- CodeExpressionStatement ces2 = new CodeExpressionStatement(exp2);
- CodeExpressionStatement ces3 = new CodeExpressionStatement(exp3);
- CodeExpressionStatement ces4 = new CodeExpressionStatement(exp4);
- CodeExpressionStatement ces5 = new CodeExpressionStatement(exp5);
-
- mymain.Statements.Add(ces1);
- mymain.Statements.Add(ces2);
- mymain.Statements.Add(ces3);
- mymain.Statements.Add(ces4);
- mymain.Statements.Add(ces5);
-
- myclass.Members.Add(mymain);
- }
Step 10
Compile the class and create the assembly.
- public void SaveAssembly() {
-
- myassembly = new CodeCompileUnit();
-
- myassembly.Namespaces.Add(mynamespace);
-
- CompilerParameters comparam = new CompilerParameters(new string[] { "mscorlib.dll" });
- comparam.ReferencedAssemblies.Add("System.dll");
- comparam.ReferencedAssemblies.Add("System.Drawing.dll");
- comparam.ReferencedAssemblies.Add("System.Windows.Forms.dll");
-
- comparam.GenerateInMemory = false;
-
- comparam.GenerateExecutable = true;
-
- comparam.MainClass = "mynamespace.CMyclass";
-
- comparam.OutputAssembly = @ "c:\temp\HelloWorld.exe";
-
- Microsoft.CSharp.CSharpCodeProvider ccp = new Microsoft.CSharp.CSharpCodeProvider();
- ICodeCompiler icc = ccp.CreateCompiler();
-
-
-
- CompilerResults compres = icc.CompileAssemblyFromDom(comparam, myassembly);
- if (compres == null || compres.Errors.Count > 0) {
- for (int i = 0; i < compres.Errors.Count; i++) {
- Console.WriteLine(compres.Errors[i]);
- }
- }
- }
Step 11
Create a test component to create the assembly.
- using System;
- namespace DynamicAssemblies {
- class testCodeDom {
- [STAThread]
- static void Main(string[] args) {
- CCodeGenerator cg = new CCodeGenerator();
- cg.CreateNamespace();
- cg.CreateImports();
- cg.CreateClass();
- cg.CreateMember();
- cg.CreateProperty();
- cg.CreateMethod();
- cg.CreateEntryPoint();
- cg.SaveAssembly();
- Console.ReadLine();
- }
- }
- }
The HelloWorld.exe would be created in the mentioned path. The output of the assembly would be as follows:
The facility to dynamically create assemblies would be of immense use when developing real-world applications that demand a great amount of flexibility and scalability.