[back]
.Net development - C# from C#
This little program I wrote (I should've called it "Yo dawg, I heard you like C#") uses a few standard .Net components to achieve the following:
create a DOM representation of a .Net class containing a method
export the DOM to a C# source code file
compile the C# source code file to a .Net DLL assembly
load the .Net DLL assembly via reflection
activate the class created earlier from the loaded assembly
invoke the method created in the first step

Essentially, this C# program creates C# source code, then compiles it, loads the compiled DLL, and then invokes a method contained within. Here are the program components, all in one file for simplicity.

The Visual Studio solution can be downloaded here. It outputs a console application.


internal static CodeNamespace CreateDOM(
string namespaceName,
string className,
string methodName
) {

// the method we're creating will simply print "Hello, world!"
CodeMethodInvokeExpression expression = new CodeMethodInvokeExpression(
new CodeTypeReferenceExpression("System.Console"),
"WriteLine",
new CodePrimitiveExpression("Hello, world!")
);

CodeMemberMethod myNewMethod = new CodeMemberMethod();
myNewMethod.Name = "PrintHello";
myNewMethod.ReturnType = new CodeTypeReference(typeof(void));
myNewMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;
myNewMethod.Statements.Add(expression);

CodeTypeDeclaration myNewClass = new CodeTypeDeclaration(className);
myNewClass.Members.Add(myNewMethod);

CodeNamespace myNewNamespace = new CodeNamespace(namespaceName);
myNewNamespace.Imports.Add(new CodeNamespaceImport("System"));
myNewNamespace.Types.Add(myNewClass);

return myNewNamespace;
}

This method programatically creates a statement (which is an invoke expression), referencing System.Console.WriteLine, passing in the string "Hello, world!".
It then puts the expression inside a new method, which it puts inside a new class, which it puts in a new namespace, which is ultimately returned. Note how the namespace references the System namespace, so that its containing types have access to the Console class.


internal static void SaveDOMToSourceFile(
CodeNamespace newNamepace,
string outputSourceFileName
) {

CodeCompileUnit compileUnit = new CodeCompileUnit();
compileUnit.Namespaces.Add(newNamepace);

CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
CodeGeneratorOptions options = new CodeGeneratorOptions();
options.BracingStyle = "C";
using (StreamWriter sourceWriter = new StreamWriter(outputSourceFileName)) {
provider.GenerateCodeFromCompileUnit(
compileUnit,
sourceWriter,
options
);
}
}

This method simply takes a CodeNamespace and outputs its contents as C# source code to a file. It works with the CodeNamespace output from the previous method.


internal static void CompileAssemblyFromSourceFile(
string sourceFileName,
string outputAssemblyFileName
) {

CompilerParameters cp = new CompilerParameters();
cp.ReferencedAssemblies.Add("System.dll");
cp.GenerateExecutable = false;
cp.OutputAssembly = outputAssemblyFileName;

// Save the assembly as a physical file.
cp.GenerateInMemory = false;

CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerResults cr = provider.CompileAssemblyFromFile(cp, sourceFileName);

if (cr.Errors.Count > 0) {
throw new Exception("Couldn't compile successfully");
}
}

This method compiles a C# source code file to a .Net DLL assembly. It works with the C# source code file created by the previous method.


static void Main(string[] args) {
string namespaceName = "SM";
string className = "Hello";
string methodName = "PrintHello";

CodeNamespace myNewNamespace = CreateDOM(
namespaceName,
className,
methodName
);

SaveDOMToSourceFile(myNewNamespace, "SM.cs");
CompileAssemblyFromSourceFile("SM.cs", "SM.dll");

// now load the assembly we've just created
Assembly assembly = Assembly.Load("SM");
string fullClassName = namespaceName + "." + className;
// and use reflection to call our method
Type type = assembly.GetType(fullClassName);
MethodInfo method = type.GetMethod(methodName);
object obj = Activator.CreateInstance(type);
method.Invoke(obj, null);

Console.ReadLine();
}

The main method should be fairly simple to follow. It chains the helper methods to create SM.dll. After that, it uses reflection to load SM.dll, activate the class Hello, and then call the method PrintHello.