C# has a pretty neat namespace named "System.Reflection.Emit", which allows you to build assemblies at runtime using Intermediate Language. This is a basic example of how to create an assembly, define a global function, and execute that function.

The following is an example. It is designed to be easy to follow, but here is a quick overview. The first thing we do is define a dynamic assembly. Within that assembly, we define a module. The whole assembly vs. module thing can be a little confusing. An assembly contains the manifest, and a collection of modules. Now in practice, an assembly usually only has a single module defined inside of it. Any C# project you create will also follow this 1 to 1 relationship. The module contains all of the actual code. Within the module, we define a global function (unsupported by C#, but IL is cool with it). Using the IL generator on the function, we can load a string onto the evaluation stack ("hello world"), call a function (Console.WriteLine), and then return from our function.

raw

namespace ReflectionEmitHelloWorld
{
    using System;
    using System.Reflection;
    using System.Reflection.Emit;

    class Program
    {
        static void Main(string[] args)
        {
            // Define the assembly and module
            const string AssemblyName = "Badflyer.HelloWorld.dll";
            var assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(AssemblyName), AssemblyBuilderAccess.RunAndCollect);
            var module = assembly.DefineDynamicModule(AssemblyName);

            // Define the method and get the Intermediate Language generator.
            var methodBuilder = module.DefineGlobalMethod("HelloWorld", MethodAttributes.Final | MethodAttributes.Public | MethodAttributes.Static, typeof(void), new Type[0]);
            var ilGenerator = methodBuilder.GetILGenerator();

            //// Too easy - but this works too
            // ilGenerator.EmitWriteLine("Hello World");

            // Get the method info for writeline
            var writeline = typeof(Console).GetMethod("WriteLine", BindingFlags.Public | BindingFlags.Static, Type.DefaultBinder, new[] { typeof(string) }, null);

            // Load the string onto the evaluation stack
            ilGenerator.Emit(OpCodes.Ldstr, "Hello World");

            // Call writeline
            ilGenerator.EmitCall(OpCodes.Call, writeline, new[] { typeof(string) });

            // Return
            ilGenerator.Emit(OpCodes.Ret);

            // Create the global functions (since that is what we defined)
            module.CreateGlobalFunctions();

            // Get our new method info
            var helloWorld = module.GetMethod("HelloWorld");
            // Invoke our new method.

            helloWorld.Invoke(null, null);

            // Wait for input
            Console.ReadLine();
        }
    }
}

Pretty cool! You can do things in IL which you cannot do from C# directly. Here for example we defined a global function which is not the member of any class. Following is the resulting IL which was generated from saving our generated .dll. Just a warning, dotnet core does not allow you to save the generated .dll. The regular .NET framework does.

raw

// To Save: var assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(AssemblyName), AssemblyBuilderAccess.RunAndSave);
// To Save: assembly.Save(AssemblyName);
.method public final static 
 void HelloWorld () cil managed 
{
 // Method begins at RVA 0x2050
 // Code size 11 (0xb)
 .maxstack 1
 IL_0000: ldstr "Hello World"
 IL_0005: call void [mscorlib]System.Console::WriteLine(string)
 IL_000a: ret
}

And that's all there is to it! You can do a lot of learning by writing C# code snippets, and viewing the generated IL within the .dll with a tool. (For example ILSpy).