When I wrote compilers, I wrote in assembly language (that is, in the source code of the assembler), after which I ran the system assembler. That way, I could easily understand what I was creating. It's much easier to read mov ax, bx (x86 build) than to decode HEX opcodes.
If I was not allowed to use assembler in the final product, I developed the compiler using the output from the assembly, and then, as soon as I got everything that worked, I made a binary output path. The beauty was, all I had to change was the actual output of bytes (opcodes and binary values, not text).
I would suggest doing something similar for your project. First, design it for the MSIL output, which you can build using ILASM. This way you can easily check your code generator output by reading the generated code. Once you are sure that your code generator is working, add an output parameter that will use Reflection.Emit or Common Compiler Infrastructure.
source share