Build-time code generation is a really powerful way to automate repetitive parts of your code. It can save time, reduce frustration, and eliminate a source of copy/paste bugs. This is something I’m familiar with due to my past work on MonoDevelop’s tooling for ASP.NET, T4 and Moonlight, and designing and/or implementing similar systems for Xamarin.iOS and Xamarin.Android. However, I haven’t seen any good documentation on it, so I decided to write an article to outline the basics.
@mhutch @KirillOsenkov @mihamarkic @xunit (this time posted from the correct account 😂)
Funny historical fact: Jim and I created xUnit.net while working on CodePlex, so we were obviously the first internal adopters, but the first team outside of us that adopted xUnit.net internally was... Roslyn!
@bradwilson @KirillOsenkov @mihamarkic @xunit
My post was targeted at generating source code based on files that are not source code, e.g. generate serialization wrappers for a json file or something. Doing it this way has similar perf and complexity as Roslyn generators, and if you use CodeDOM you can support every language with a CodeDOM provider.
@bradwilson @KirillOsenkov @mihamarkic @xunit
Generating code for your assembly *based on code in your assembly* is much more difficult. You can use a two pass compile and reflection, or parse the source code yourself, but both these options really hurt build perf and are difficult to get right.
Generators are by far the best way to handle this but are Roslyn only. AFAIK there is no good solution to this class of problem that supports all languages.
@bradwilson @KirillOsenkov @mihamarkic @xunit
FWIW, generators also only solve the problem of letting you inspect parsed/resolved code and inject source code text into the compilation. There isn't actually any mechanism for generating the source code text. Personally, I would use either preprocessed T4 templates or CodeDOM for the actual generation part of a generator.
@bradwilson @KirillOsenkov @mihamarkic @xunit
If I were to implement code-dependent generation logic for both Roslyn and non-Roslyn languages, I would have two-pass compile targets for non-Roslyn languages with a reflection-based generator task, and have a generator for Roslyn languages. These would convert the Roslyn/reflection data into a shared model, then use a shared implementation to process that data and generate code via CodeDOM.
Not elegant, but I can't think of anything better 😞