{"id":29404,"date":"2020-08-25T06:31:10","date_gmt":"2020-08-25T13:31:10","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=29404"},"modified":"2020-08-26T08:35:44","modified_gmt":"2020-08-26T15:35:44","slug":"new-c-source-generator-samples","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/new-c-source-generator-samples\/","title":{"rendered":"New C# Source Generator Samples"},"content":{"rendered":"<p>Phillip introduced C# Source Generators <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/introducing-c-source-generators\/\" rel=\"nofollow\">here<\/a>. This post describes two new generators that we added to the <a href=\"https:\/\/github.com\/dotnet\/roslyn-sdk\/tree\/master\/samples\/CSharp\/SourceGenerators\">samples project<\/a> in the Roslyn SDK github repo.<\/p>\n<p>The first generator gives you strongly typed access to <a href=\"https:\/\/en.wikipedia.org\/wiki\/Comma-separated_values\" rel=\"nofollow\">CSV<\/a> data. The second one creates string constants based on <a href=\"https:\/\/mustache.github.io\/mustache.5.html\" rel=\"nofollow\">Mustache<\/a> specifications.<\/p>\n<h2><a id=\"user-content-source-generators-overview\" class=\"anchor\" href=\"#source-generators-overview\" aria-hidden=\"true\"><\/a>Source Generators Overview<\/h2>\n<p>It is important to have a good mental picture of how source generators operate. Conceptually, a generator is a function that takes some input (more on that later) and generates C# code as output. This &#8216;function&#8217; runs <em>before<\/em> the code for the main project is compiled. In fact, its output becomes part of the project.<\/p>\n<p>The inputs to a generator must be available at compile time, because that&#8217;s when generators run. In this post we explore two different ways to provide it.<\/p>\n<p>You use a generator in your project by either referencing a generator project or by referencing the generator assembly directly. In the samples project this is achieved by the following instruction in the project file:<\/p>\n<div class=\"highlight highlight-text-xml\">\n<pre>&lt;<span class=\"pl-ent\">ItemGroup<\/span>&gt;\r\n    &lt;<span class=\"pl-ent\">ProjectReference<\/span> <span class=\"pl-e\">Include<\/span>=<span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span>..\\SourceGeneratorSamples\\SourceGeneratorSamples.csproj<span class=\"pl-pds\">\"<\/span><\/span>\r\n                            <span class=\"pl-e\">OutputItemType<\/span>=<span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span>Analyzer<span class=\"pl-pds\">\"<\/span><\/span> <span class=\"pl-e\">ReferenceOutputAssembly<\/span>=<span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span>false<span class=\"pl-pds\">\"<\/span><\/span> \/&gt;\r\n&lt;\/<span class=\"pl-ent\">ItemGroup<\/span>&gt;<\/pre>\n<\/div>\n<h2><a id=\"user-content-csv-generator-usage\" class=\"anchor\" href=\"#csv-generator-usage\" aria-hidden=\"true\"><\/a>CSV Generator Usage<\/h2>\n<p>The CSV Generator takes as an input CSV files and returns strongly typed C# representations of them as output. You specify the CSV files with the following lines in the project file:<\/p>\n<div class=\"highlight highlight-text-xml\">\n<pre>&lt;<span class=\"pl-ent\">ItemGroup<\/span>&gt;\r\n    &lt;<span class=\"pl-ent\">AdditionalFiles<\/span> <span class=\"pl-e\">Include<\/span>=<span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span>People.csv<span class=\"pl-pds\">\"<\/span><\/span> <span class=\"pl-e\">CsvLoadType<\/span>=<span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span>Startup<span class=\"pl-pds\">\"<\/span><\/span> \/&gt;\r\n    &lt;<span class=\"pl-ent\">AdditionalFiles<\/span> <span class=\"pl-e\">Include<\/span>=<span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span>Cars.csv<span class=\"pl-pds\">\"<\/span><\/span> <span class=\"pl-e\">CsvLoadType<\/span>=<span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span>OnDemand<span class=\"pl-pds\">\"<\/span><\/span> <span class=\"pl-e\">CacheObjects<\/span>=<span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span>true<span class=\"pl-pds\">\"<\/span><\/span> \/&gt;\r\n&lt;\/<span class=\"pl-ent\">ItemGroup<\/span>&gt;<\/pre>\n<\/div>\n<p>Where the <code>People.csv<\/code> file looks like so:<\/p>\n<pre lang=\"csv\"><code>Name, address, 11Age\r\n\"Luca Bol\", \"23 Bell Street\", 90\r\n\"john doe\", \"32 Carl street\", 45\r\n<\/code><\/pre>\n<p>There are two additional arguments that get passed as part of the input in the project file <code>AdditionalFiles<\/code> tag: <code>CsvLoadType<\/code> and <code>CacheObjects<\/code>. <code>CsvLoadType<\/code> can take the value of <code>Startup<\/code> or <code>OnDemand<\/code>: the former instruct the code to load the objects representing the CSV file when the program starts; the latter loads them at first usage. <code>CacheObjects<\/code> is a <code>bool<\/code> indicating if the objects need to be cached after creation.<\/p>\n<p>It can be a little confusing to keep straight when exactly every phase runs. The generation of classes representing the shape of the CSV file happens at <em>compile time<\/em>, while the creation of the objects for each row of the file happens at <em>run time<\/em> according to the policy specified by <code>CsvLoadType<\/code> and <code>CacheObjects<\/code>.<\/p>\n<p>BTW: the <code>11Age<\/code> column name came about as a way to test that the C# generation is correct in case of columns starting with a number.<\/p>\n<p>Given such input, the generator creates a <code>CSV<\/code> namespace that you can import in your code with:<\/p>\n<div class=\"highlight highlight-source-cs\">\n<pre><span class=\"pl-k\">using<\/span> <span class=\"pl-en\">CSV<\/span>;<\/pre>\n<\/div>\n<p>In the namespace there is one class for each CSV file. Each class contains an <code>All<\/code> static property that can be used like so:<\/p>\n<div class=\"highlight highlight-source-cs\">\n<pre><span class=\"pl-en\">WriteLine<\/span>(<span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span>## CARS<span class=\"pl-pds\">\"<\/span><\/span>);\r\n<span class=\"pl-smi\">Cars<\/span>.<span class=\"pl-smi\">All<\/span>.<span class=\"pl-en\">ToList<\/span>().<span class=\"pl-en\">ForEach<\/span>(<span class=\"pl-smi\">c<\/span> <span class=\"pl-k\">=&gt;<\/span> <span class=\"pl-en\">WriteLine<\/span>(<span class=\"pl-s\"><span class=\"pl-pds\">$\"<\/span>{<span class=\"pl-smi\">c<\/span>.<span class=\"pl-smi\">Brand<\/span>}<span class=\"pl-cce\">\\t<\/span>{<span class=\"pl-smi\">c<\/span>.<span class=\"pl-smi\">Model<\/span>}<span class=\"pl-cce\">\\t<\/span>{<span class=\"pl-smi\">c<\/span>.<span class=\"pl-smi\">Year<\/span>}<span class=\"pl-cce\">\\t<\/span>{<span class=\"pl-smi\">c<\/span>.<span class=\"pl-smi\">Cc<\/span>}<span class=\"pl-pds\">\"<\/span><\/span>));\r\n<span class=\"pl-en\">WriteLine<\/span>(<span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span><span class=\"pl-cce\">\\n<\/span>## PEOPLE<span class=\"pl-pds\">\"<\/span><\/span>);\r\n<span class=\"pl-smi\">People<\/span>.<span class=\"pl-smi\">All<\/span>.<span class=\"pl-en\">ToList<\/span>().<span class=\"pl-en\">ForEach<\/span>(<span class=\"pl-smi\">p<\/span> <span class=\"pl-k\">=&gt;<\/span> <span class=\"pl-en\">WriteLine<\/span>(<span class=\"pl-s\"><span class=\"pl-pds\">$\"<\/span>{<span class=\"pl-smi\">p<\/span>.<span class=\"pl-smi\">Name<\/span>}<span class=\"pl-cce\">\\t<\/span>{<span class=\"pl-smi\">p<\/span>.<span class=\"pl-smi\">Address<\/span>}<span class=\"pl-cce\">\\t<\/span>{<span class=\"pl-smi\">p<\/span>.<span class=\"pl-smi\">_11Age<\/span>}<span class=\"pl-pds\">\"<\/span><\/span>));<\/pre>\n<\/div>\n<p>So that&#8217;s how you use the generator. Let&#8217;s now look at how it is implemented.<\/p>\n<h2><a id=\"user-content-csv-generator-implementation\" class=\"anchor\" href=\"#csv-generator-implementation\" aria-hidden=\"true\"><\/a>CSV Generator Implementation<\/h2>\n<p>Inside the <a href=\"https:\/\/github.com\/dotnet\/roslyn-sdk\/tree\/master\/samples\/CSharp\/SourceGenerators\/SourceGeneratorSamples\">generator project<\/a> you need a class implementing the <code>ISourceGenerator<\/code> interface with a <code>Generator<\/code> attribute.<\/p>\n<div class=\"highlight highlight-source-cs\">\n<pre>[<span class=\"pl-en\">Generator<\/span>]\r\n<span class=\"pl-k\">public<\/span> <span class=\"pl-k\">class<\/span> <span class=\"pl-en\">CSVGenerator<\/span> : <span class=\"pl-en\">ISourceGenerator<\/span><\/pre>\n<\/div>\n<p>The <code>Execute<\/code> method is the entry point. It gets called by the compiler to start the generation process. Ours looks like this:<\/p>\n<div class=\"highlight highlight-source-cs\">\n<pre><span class=\"pl-k\">public<\/span> <span class=\"pl-k\">void<\/span> <span class=\"pl-en\">Execute<\/span>(<span class=\"pl-en\">SourceGeneratorContext<\/span> <span class=\"pl-smi\">context<\/span>)\r\n{\r\n    <span class=\"pl-en\">IEnumerable<\/span>&lt;(<span class=\"pl-en\">CsvLoadType<\/span>, <span class=\"pl-k\">bool<\/span>, <span class=\"pl-en\">AdditionalText<\/span>)&gt; <span class=\"pl-smi\">options<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-en\">GetLoadOptions<\/span>(<span class=\"pl-smi\">context<\/span>);\r\n    <span class=\"pl-en\">IEnumerable<\/span>&lt;(<span class=\"pl-k\">string<\/span>, <span class=\"pl-k\">string<\/span>)&gt; <span class=\"pl-smi\">nameCodeSequence<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-en\">SourceFilesFromAdditionalFiles<\/span>(<span class=\"pl-smi\">options<\/span>);\r\n    <span class=\"pl-k\">foreach<\/span> ((<span class=\"pl-k\">string<\/span> <span class=\"pl-en\">name<\/span>, <span class=\"pl-k\">string<\/span> <span class=\"pl-en\">code<\/span>) <span class=\"pl-k\">in<\/span> <span class=\"pl-smi\">nameCodeSequence<\/span>)\r\n        <span class=\"pl-smi\">context<\/span>.<span class=\"pl-en\">AddSource<\/span>(<span class=\"pl-s\"><span class=\"pl-pds\">$\"<\/span>Csv_{<span class=\"pl-smi\">name<\/span>}<span class=\"pl-pds\">\"<\/span><\/span>, <span class=\"pl-smi\">SourceText<\/span>.<span class=\"pl-en\">From<\/span>(<span class=\"pl-smi\">code<\/span>, <span class=\"pl-smi\">Encoding<\/span>.<span class=\"pl-smi\">UTF8<\/span>));\r\n}<\/pre>\n<\/div>\n<p>We first get the options &#8211; <code>CsvLoadType<\/code> and <code>CacheObjects<\/code> from the project file &#8211; we then generate the source files by reading the additional files and add them to the project.<\/p>\n<p>Getting the options is just a few easy calls to the analyzer apis:<\/p>\n<div class=\"highlight highlight-source-cs\">\n<pre><span class=\"pl-k\">static<\/span> <span class=\"pl-en\">IEnumerable<\/span>&lt;(<span class=\"pl-en\">CsvLoadType<\/span>, <span class=\"pl-k\">bool<\/span>, <span class=\"pl-en\">AdditionalText<\/span>)&gt; <span class=\"pl-en\">GetLoadOptions<\/span>(<span class=\"pl-en\">SourceGeneratorContext<\/span> <span class=\"pl-smi\">context<\/span>)\r\n{\r\n    <span class=\"pl-k\">foreach<\/span> (<span class=\"pl-en\">AdditionalText<\/span> <span class=\"pl-smi\">file<\/span> <span class=\"pl-k\">in<\/span> <span class=\"pl-smi\">context<\/span>.<span class=\"pl-smi\">AdditionalFiles<\/span>)\r\n    {\r\n        <span class=\"pl-k\">if<\/span> (<span class=\"pl-smi\">Path<\/span>.<span class=\"pl-en\">GetExtension<\/span>(<span class=\"pl-smi\">file<\/span>.<span class=\"pl-smi\">Path<\/span>).<span class=\"pl-en\">Equals<\/span>(<span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span>.csv<span class=\"pl-pds\">\"<\/span><\/span>, <span class=\"pl-smi\">StringComparison<\/span>.<span class=\"pl-smi\">OrdinalIgnoreCase<\/span>))\r\n        {\r\n            <span class=\"pl-c\"><span class=\"pl-c\">\/\/<\/span> are there any options for it?<\/span>\r\n            <span class=\"pl-smi\">context<\/span>.<span class=\"pl-smi\">AnalyzerConfigOptions<\/span>.<span class=\"pl-en\">GetOptions<\/span>(<span class=\"pl-smi\">file<\/span>)\r\n                .<span class=\"pl-en\">TryGetValue<\/span>(<span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span>build_metadata.additionalfiles.CsvLoadType<span class=\"pl-pds\">\"<\/span><\/span>, <span class=\"pl-k\">out<\/span> <span class=\"pl-k\">string<\/span>? <span class=\"pl-smi\">loadTimeString<\/span>);\r\n            <span class=\"pl-smi\">Enum<\/span>.<span class=\"pl-en\">TryParse<\/span>(<span class=\"pl-smi\">loadTimeString<\/span>, <span class=\"pl-smi\">ignoreCase<\/span>: <span class=\"pl-c1\">true<\/span>, <span class=\"pl-k\">out<\/span> <span class=\"pl-en\">CsvLoadType<\/span> <span class=\"pl-smi\">loadType<\/span>);\r\n\r\n            <span class=\"pl-smi\">context<\/span>.<span class=\"pl-smi\">AnalyzerConfigOptions<\/span>.<span class=\"pl-en\">GetOptions<\/span>(<span class=\"pl-smi\">file<\/span>)\r\n                .<span class=\"pl-en\">TryGetValue<\/span>(<span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span>build_metadata.additionalfiles.CacheObjects<span class=\"pl-pds\">\"<\/span><\/span>, <span class=\"pl-k\">out<\/span> <span class=\"pl-k\">string<\/span>? <span class=\"pl-smi\">cacheObjectsString<\/span>);\r\n            <span class=\"pl-smi\">bool<\/span>.<span class=\"pl-en\">TryParse<\/span>(<span class=\"pl-smi\">cacheObjectsString<\/span>, <span class=\"pl-k\">out<\/span> <span class=\"pl-k\">bool<\/span> <span class=\"pl-smi\">cacheObjects<\/span>);\r\n\r\n            <span class=\"pl-k\">yield<\/span> <span class=\"pl-k\">return<\/span> (<span class=\"pl-smi\">loadType<\/span>, <span class=\"pl-smi\">cacheObjects<\/span>, <span class=\"pl-smi\">file<\/span>);\r\n        }\r\n    }\r\n}<\/pre>\n<\/div>\n<p>Once the options are retrieved, the process of generating C# source files to represent the CSV data can start.<\/p>\n<div class=\"highlight highlight-source-cs\">\n<pre><span class=\"pl-k\">static<\/span> <span class=\"pl-en\">IEnumerable<\/span>&lt;(<span class=\"pl-k\">string<\/span>, <span class=\"pl-k\">string<\/span>)&gt; <span class=\"pl-en\">SourceFilesFromAdditionalFile<\/span>(<span class=\"pl-en\">CsvLoadType<\/span> <span class=\"pl-smi\">loadTime<\/span>,\r\n    <span class=\"pl-k\">bool<\/span> <span class=\"pl-smi\">cacheObjects<\/span>, <span class=\"pl-en\">AdditionalText<\/span> <span class=\"pl-smi\">file<\/span>)\r\n{\r\n    <span class=\"pl-k\">string<\/span> <span class=\"pl-smi\">className<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-smi\">Path<\/span>.<span class=\"pl-en\">GetFileNameWithoutExtension<\/span>(<span class=\"pl-smi\">file<\/span>.<span class=\"pl-smi\">Path<\/span>);\r\n    <span class=\"pl-k\">string<\/span> <span class=\"pl-smi\">csvText<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-smi\">file<\/span>.<span class=\"pl-en\">GetText<\/span>()<span class=\"pl-k\">!<\/span>.<span class=\"pl-en\">ToString<\/span>();\r\n    <span class=\"pl-k\">return<\/span> <span class=\"pl-k\">new<\/span> (<span class=\"pl-k\">string<\/span>, <span class=\"pl-k\">string<\/span>)[] { (<span class=\"pl-smi\">className<\/span>, <span class=\"pl-en\">GenerateClassFile<\/span>(<span class=\"pl-smi\">className<\/span>, <span class=\"pl-smi\">csvText<\/span>, <span class=\"pl-smi\">loadTime<\/span>, <span class=\"pl-smi\">cacheObjects<\/span>)) };\r\n}\r\n\r\n<span class=\"pl-k\">static<\/span> <span class=\"pl-en\">IEnumerable<\/span>&lt;(<span class=\"pl-k\">string<\/span>, <span class=\"pl-k\">string<\/span>)&gt; <span class=\"pl-en\">SourceFilesFromAdditionalFiles<\/span>(IEnumerable&lt;(<span class=\"pl-en\">CsvLoadType<\/span> <span class=\"pl-smi\">loadTime<\/span>,\r\n    <span class=\"pl-k\">bool<\/span> <span class=\"pl-smi\">cacheObjects<\/span>, <span class=\"pl-en\">AdditionalText<\/span> <span class=\"pl-smi\">file<\/span>)&gt; pathsData)\r\n    <span class=\"pl-k\">=&gt;<\/span> <span class=\"pl-smi\">pathsData<\/span>.<span class=\"pl-en\">SelectMany<\/span>(<span class=\"pl-smi\">d<\/span> <span class=\"pl-k\">=&gt;<\/span> <span class=\"pl-en\">SourceFilesFromAdditionalFile<\/span>(<span class=\"pl-smi\">d<\/span>.<span class=\"pl-smi\">loadTime<\/span>, <span class=\"pl-smi\">d<\/span>.<span class=\"pl-smi\">cacheObjects<\/span>, <span class=\"pl-smi\">d<\/span>.<span class=\"pl-smi\">file<\/span>));<\/pre>\n<\/div>\n<p>We iterate over all the CSV files and generate a class file for each one of them by calling <code>GenerateClassFile<\/code>. This is where the magic happens: we look at the csv content and we generate the correct class file to add to the project.<\/p>\n<p>But this is a long function (<a href=\"https:\/\/github.com\/dotnet\/roslyn-sdk\/blob\/master\/samples\/CSharp\/SourceGenerators\/SourceGeneratorSamples\/CsvGenerator.cs\">code<\/a>), so let&#8217;s just look at the start and the end of it to get the flavour.<\/p>\n<div class=\"highlight highlight-source-cs\">\n<pre><span class=\"pl-k\">public<\/span> <span class=\"pl-k\">static<\/span> <span class=\"pl-k\">string<\/span> <span class=\"pl-en\">GenerateClassFile<\/span>(<span class=\"pl-k\">string<\/span> <span class=\"pl-smi\">className<\/span>, <span class=\"pl-k\">string<\/span> <span class=\"pl-smi\">csvText<\/span>, <span class=\"pl-en\">CsvLoadType<\/span> <span class=\"pl-smi\">loadTime<\/span>,\r\n    <span class=\"pl-k\">bool<\/span> <span class=\"pl-smi\">cacheObjects<\/span>)\r\n{\r\n    <span class=\"pl-en\">StringBuilder<\/span> <span class=\"pl-smi\">sb<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-k\">new<\/span> <span class=\"pl-en\">StringBuilder<\/span>();\r\n    <span class=\"pl-smi\">using<\/span> <span class=\"pl-en\">CsvTextFieldParser<\/span> <span class=\"pl-smi\">parser<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-k\">new<\/span> <span class=\"pl-en\">CsvTextFieldParser<\/span>(<span class=\"pl-k\">new<\/span> <span class=\"pl-en\">StringReader<\/span>(<span class=\"pl-smi\">csvText<\/span>));\r\n\r\n    <span class=\"pl-c\"><span class=\"pl-c\">\/\/<\/span>\/\/ Usings<\/span>\r\n    <span class=\"pl-smi\">sb<\/span>.<span class=\"pl-en\">Append<\/span>(<span class=\"pl-s\"><span class=\"pl-pds\">@\"<\/span><\/span>\r\n<span class=\"pl-s\">#nullable enable<\/span>\r\n<span class=\"pl-s\">namespace CSV {<\/span>\r\n<span class=\"pl-s\">using System.Collections.Generic;<\/span>\r\n\r\n<span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span><\/span>);\r\n    <span class=\"pl-c\"><span class=\"pl-c\">\/\/<\/span>\/\/ Class Definition<\/span>\r\n    <span class=\"pl-smi\">sb<\/span>.<span class=\"pl-en\">Append<\/span>(<span class=\"pl-s\"><span class=\"pl-pds\">$\"<\/span>    public class {<span class=\"pl-smi\">className<\/span>} {{<span class=\"pl-cce\">\\n<\/span><span class=\"pl-pds\">\"<\/span><\/span>);<\/pre>\n<\/div>\n<p>First we add a new class to the <code>CSV<\/code> namespace. The name of the class corresponds to the CSV file name. Then we generate the code for the class and return it.<\/p>\n<div class=\"highlight highlight-source-cs\">\n<pre>    <span class=\"pl-c\"><span class=\"pl-c\">\/\/<\/span> CODE TO GENERATE C# FROM THE CSV FILE ...<\/span>\r\n\r\n    <span class=\"pl-smi\">sb<\/span>.<span class=\"pl-en\">Append<\/span>(<span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span>            }<span class=\"pl-cce\">\\n<\/span>        }<span class=\"pl-cce\">\\n<\/span>    }<span class=\"pl-cce\">\\n<\/span>}<span class=\"pl-cce\">\\n<\/span><span class=\"pl-pds\">\"<\/span><\/span>);\r\n    <span class=\"pl-k\">return<\/span> <span class=\"pl-smi\">sb<\/span>.<span class=\"pl-en\">ToString<\/span>();\r\n}<\/pre>\n<\/div>\n<p>In the end, the compiler adds to our project\na file called <code>Csv_People.cs<\/code> containing the code below.<\/p>\n<div class=\"highlight highlight-source-cs\">\n<pre>#nullable enable\r\n<span class=\"pl-k\">namespace<\/span> <span class=\"pl-en\">CSV<\/span> {\r\n    <span class=\"pl-k\">using<\/span> <span class=\"pl-en\">System<\/span>.<span class=\"pl-en\">Collections<\/span>.<span class=\"pl-en\">Generic<\/span>;\r\n\r\n    <span class=\"pl-k\">public<\/span> <span class=\"pl-k\">class<\/span> <span class=\"pl-en\">People<\/span> {\r\n\r\n        <span class=\"pl-k\">static<\/span> <span class=\"pl-en\">People<\/span>() { <span class=\"pl-k\">var<\/span> <span class=\"pl-smi\">x<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-smi\">All<\/span>; }\r\n        <span class=\"pl-k\">public<\/span> <span class=\"pl-k\">string<\/span> <span class=\"pl-smi\">Name<\/span> { <span class=\"pl-k\">get<\/span>; <span class=\"pl-k\">set<\/span>;} <span class=\"pl-k\">=<\/span> <span class=\"pl-smi\">default<\/span><span class=\"pl-k\">!<\/span>;\r\n        <span class=\"pl-k\">public<\/span> <span class=\"pl-k\">string<\/span> <span class=\"pl-smi\">Address<\/span> { <span class=\"pl-k\">get<\/span>; <span class=\"pl-k\">set<\/span>;} <span class=\"pl-k\">=<\/span> <span class=\"pl-smi\">default<\/span><span class=\"pl-k\">!<\/span>;\r\n        <span class=\"pl-k\">public<\/span> <span class=\"pl-k\">int<\/span> <span class=\"pl-smi\">_11Age<\/span> { <span class=\"pl-k\">get<\/span>; <span class=\"pl-k\">set<\/span>;} <span class=\"pl-k\">=<\/span> <span class=\"pl-smi\">default<\/span><span class=\"pl-k\">!<\/span>;\r\n\r\n        <span class=\"pl-k\">static<\/span> <span class=\"pl-en\">IEnumerable<\/span>&lt;<span class=\"pl-en\">People<\/span>&gt;? <span class=\"pl-smi\">_all<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-c1\">null<\/span>;\r\n\r\n        <span class=\"pl-k\">public<\/span> <span class=\"pl-k\">static<\/span> <span class=\"pl-en\">IEnumerable<\/span>&lt;<span class=\"pl-en\">People<\/span>&gt; <span class=\"pl-smi\">All<\/span> {\r\n            <span class=\"pl-k\">get<\/span> {\r\n\r\n                <span class=\"pl-en\">List<\/span>&lt;<span class=\"pl-en\">People<\/span>&gt; <span class=\"pl-smi\">l<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-k\">new<\/span> <span class=\"pl-en\">List<\/span>&lt;<span class=\"pl-en\">People<\/span>&gt;();\r\n                <span class=\"pl-en\">People<\/span> <span class=\"pl-smi\">c<\/span>;\r\n                <span class=\"pl-smi\">c<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-k\">new<\/span> <span class=\"pl-en\">People<\/span>();\r\n                <span class=\"pl-smi\">c<\/span>.<span class=\"pl-smi\">Name<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span>Luca Bol<span class=\"pl-pds\">\"<\/span><\/span>;\r\n                <span class=\"pl-smi\">c<\/span>.<span class=\"pl-smi\">Address<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span>23 Bell Street<span class=\"pl-pds\">\"<\/span><\/span>;\r\n                <span class=\"pl-smi\">c<\/span>.<span class=\"pl-smi\">_11Age<\/span> <span class=\"pl-k\">=<\/span>  <span class=\"pl-c1\">90<\/span>;\r\n                <span class=\"pl-smi\">l<\/span>.<span class=\"pl-en\">Add<\/span>(<span class=\"pl-smi\">c<\/span>);\r\n                <span class=\"pl-smi\">c<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-k\">new<\/span> <span class=\"pl-en\">People<\/span>();\r\n                <span class=\"pl-smi\">c<\/span>.<span class=\"pl-smi\">Name<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span>john doe<span class=\"pl-pds\">\"<\/span><\/span>;\r\n                <span class=\"pl-smi\">c<\/span>.<span class=\"pl-smi\">Address<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span>32 Carl street<span class=\"pl-pds\">\"<\/span><\/span>;\r\n                <span class=\"pl-smi\">c<\/span>.<span class=\"pl-smi\">_11Age<\/span> <span class=\"pl-k\">=<\/span>  <span class=\"pl-c1\">45<\/span>;\r\n                <span class=\"pl-smi\">l<\/span>.<span class=\"pl-en\">Add<\/span>(<span class=\"pl-smi\">c<\/span>);\r\n                <span class=\"pl-smi\">_all<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-smi\">l<\/span>;\r\n                <span class=\"pl-k\">return<\/span> <span class=\"pl-smi\">l<\/span>;\r\n            }\r\n        }\r\n    }\r\n}<\/pre>\n<\/div>\n<p>This is what gets compiled into your project, so that you can reference it from the code.<\/p>\n<h2><a id=\"user-content-mustache-generator-usage\" class=\"anchor\" href=\"#mustache-generator-usage\" aria-hidden=\"true\"><\/a>Mustache Generator Usage<\/h2>\n<p>For the <a href=\"https:\/\/mustache.github.io\/mustache.5.html\" rel=\"nofollow\">Mustage Generator<\/a>, we use a different way to pass input arguments compared to the CSV Generator above.\nWe embed our input in assembly attributes and then, in the generator code, we fish them out of the assembly to\ndrive the generation process.<\/p>\n<p>In our client code, we pass the inputs to the generator as below:<\/p>\n<div class=\"highlight highlight-source-cs\">\n<pre><span class=\"pl-k\">using<\/span> <span class=\"pl-en\">Mustache<\/span>;\r\n\r\n[<span class=\"pl-k\">assembly<\/span>: <span class=\"pl-en\">Mustache<\/span>(<span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span>Lottery<span class=\"pl-pds\">\"<\/span><\/span>, <span class=\"pl-smi\">t1<\/span>, <span class=\"pl-smi\">h1<\/span>)]\r\n[<span class=\"pl-k\">assembly<\/span>: <span class=\"pl-en\">Mustache<\/span>(<span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span>HR<span class=\"pl-pds\">\"<\/span><\/span>, <span class=\"pl-smi\">t2<\/span>, <span class=\"pl-smi\">h2<\/span>)]\r\n[<span class=\"pl-k\">assembly<\/span>: <span class=\"pl-en\">Mustache<\/span>(<span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span>HTML<span class=\"pl-pds\">\"<\/span><\/span>, <span class=\"pl-smi\">t3<\/span>, <span class=\"pl-smi\">h3<\/span>)]\r\n[<span class=\"pl-k\">assembly<\/span>: <span class=\"pl-en\">Mustache<\/span>(<span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span>Section<span class=\"pl-pds\">\"<\/span><\/span>, <span class=\"pl-smi\">t4<\/span>, <span class=\"pl-smi\">h4<\/span>)]\r\n[<span class=\"pl-k\">assembly<\/span>: <span class=\"pl-en\">Mustache<\/span>(<span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span>NestedSection<span class=\"pl-pds\">\"<\/span><\/span>, <span class=\"pl-smi\">t5<\/span>, <span class=\"pl-smi\">h5<\/span>)]<\/pre>\n<\/div>\n<p>The first argument to the <code>Mustache<\/code> attribute is the name of a static property that gets generated in the <code>Mustache.Constants<\/code> class.<\/p>\n<p>The second argument represents the mustache template to use. In the demo we use the templates from the <a href=\"https:\/\/mustache.github.io\/mustache.5.html\" rel=\"nofollow\">manual<\/a>.\nFor example:<\/p>\n<div class=\"highlight highlight-source-cs\">\n<pre><span class=\"pl-k\">public<\/span> <span class=\"pl-k\">const<\/span> <span class=\"pl-k\">string<\/span> <span class=\"pl-smi\">t1<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-s\"><span class=\"pl-pds\">@\"<\/span><\/span>\r\n<span class=\"pl-s\">Hello {{name}}<\/span>\r\n<span class=\"pl-s\">You have just won {{value}} dollars!<\/span>\r\n<span class=\"pl-s\">{{#in_ca}}<\/span>\r\n<span class=\"pl-s\">Well, {{taxed_value}} dollars, after taxes.<\/span>\r\n<span class=\"pl-s\">{{\/in_ca}}<\/span>\r\n<span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span><\/span>;<\/pre>\n<\/div>\n<p>The third argument is the hash to use with the template.<\/p>\n<div class=\"highlight highlight-source-cs\">\n<pre><span class=\"pl-k\">public<\/span> <span class=\"pl-k\">const<\/span> <span class=\"pl-k\">string<\/span> <span class=\"pl-smi\">h1<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-s\"><span class=\"pl-pds\">@\"<\/span><\/span>\r\n<span class=\"pl-s\">{<\/span>\r\n<span class=\"pl-s\"><span class=\"pl-cce\">\"\"<\/span>name<span class=\"pl-cce\">\"\"<\/span>: <span class=\"pl-cce\">\"\"<\/span>Chris<span class=\"pl-cce\">\"\"<\/span>,<\/span>\r\n<span class=\"pl-s\"><span class=\"pl-cce\">\"\"<\/span>value<span class=\"pl-cce\">\"\"<\/span>: 10000,<\/span>\r\n<span class=\"pl-s\"><span class=\"pl-cce\">\"\"<\/span>taxed_value<span class=\"pl-cce\">\"\"<\/span>: 6000,<\/span>\r\n<span class=\"pl-s\"><span class=\"pl-cce\">\"\"<\/span>in_ca<span class=\"pl-cce\">\"\"<\/span>: true<\/span>\r\n<span class=\"pl-s\">}<\/span>\r\n<span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span><\/span>;<\/pre>\n<\/div>\n<p>Each attribute instance is a named pair (template, hash). Our generator uses it to generate a string constant that you can access like this:<\/p>\n<div class=\"highlight highlight-source-cs\">\n<pre><span class=\"pl-en\">WriteLine<\/span>(<span class=\"pl-smi\">Mustache<\/span>.<span class=\"pl-smi\">Constants<\/span>.<span class=\"pl-smi\">Lottery<\/span>);<\/pre>\n<\/div>\n<p>The resulting output is good for Chris, as expected:<\/p>\n<div class=\"highlight highlight-source-shell\">\n<pre>Hello Chris\r\nYou have just won 10000 dollars<span class=\"pl-k\">!<\/span>\r\nWell, 6000.0 dollars, after taxes.<\/pre>\n<\/div>\n<h2><a id=\"user-content-mustache-generator-implementation\" class=\"anchor\" href=\"#mustache-generator-implementation\" aria-hidden=\"true\"><\/a>Mustache Generator Implementation<\/h2>\n<p>The input to this generator is quite different from the previous one, but the implementation is similar. Or at least it has a familiar &#8216;shape&#8217;. As before there is a class implementing <code>ISourceGenerator<\/code> with an <code>Execute<\/code> method:<\/p>\n<div class=\"highlight highlight-source-cs\">\n<pre>[<span class=\"pl-en\">Generator<\/span>]\r\n<span class=\"pl-k\">public<\/span> <span class=\"pl-k\">class<\/span> <span class=\"pl-en\">MustacheGenerator<\/span> : <span class=\"pl-en\">ISourceGenerator<\/span>\r\n{\r\n    <span class=\"pl-k\">public<\/span> <span class=\"pl-k\">void<\/span> <span class=\"pl-en\">Execute<\/span>(<span class=\"pl-en\">SourceGeneratorContext<\/span> <span class=\"pl-smi\">context<\/span>)\r\n    {\r\n        <span class=\"pl-k\">string<\/span> <span class=\"pl-smi\">attributeSource<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-s\"><span class=\"pl-pds\">@\"<\/span><\/span>\r\n<span class=\"pl-s\">[System.AttributeUsage(System.AttributeTargets.Assembly, AllowMultiple=true)]<\/span>\r\n<span class=\"pl-s\">internal sealed class MustacheAttribute: System.Attribute<\/span>\r\n<span class=\"pl-s\">{<\/span>\r\n<span class=\"pl-s\">    public string Name { get; }<\/span>\r\n<span class=\"pl-s\">    public string Template { get; }<\/span>\r\n<span class=\"pl-s\">    public string Hash { get; }<\/span>\r\n<span class=\"pl-s\">    public MustacheAttribute(string name, string template, string hash)<\/span>\r\n<span class=\"pl-s\">        =&gt; (Name, Template, Hash) = (name, template, hash);<\/span>\r\n<span class=\"pl-s\">}<\/span>\r\n<span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span><\/span>;\r\n        <span class=\"pl-smi\">context<\/span>.<span class=\"pl-en\">AddSource<\/span>(<span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span>Mustache_MainAttributes__<span class=\"pl-pds\">\"<\/span><\/span>, <span class=\"pl-smi\">SourceText<\/span>.<span class=\"pl-en\">From<\/span>(<span class=\"pl-smi\">attributeSource<\/span>, <span class=\"pl-smi\">Encoding<\/span>.<span class=\"pl-smi\">UTF8<\/span>));<\/pre>\n<\/div>\n<p>First we need to add a source file to the project to define the Mustache attribute that will be used by the clients to specify the inputs.<\/p>\n<p>Then we inspect the assembly to fish out all the usages of the <code>Mustache<\/code> attribute.<\/p>\n<div class=\"highlight highlight-source-cs\">\n<pre>        <span class=\"pl-en\">Compilation<\/span> <span class=\"pl-smi\">compilation<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-smi\">context<\/span>.<span class=\"pl-smi\">Compilation<\/span>;\r\n\r\n        <span class=\"pl-en\">IEnumerable<\/span>&lt;(<span class=\"pl-k\">string<\/span>, <span class=\"pl-k\">string<\/span>, <span class=\"pl-k\">string<\/span>)&gt; <span class=\"pl-smi\">options<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-en\">GetMustacheOptions<\/span>(<span class=\"pl-smi\">compilation<\/span>);<\/pre>\n<\/div>\n<p>The code to do so is in the <code>GetMustacheOptions<\/code> function, that you can inspect <a href=\"https:\/\/github.com\/dotnet\/roslyn-sdk\/blob\/master\/samples\/CSharp\/SourceGenerators\/SourceGeneratorSamples\/MustacheGenerator.cs\">here<\/a>.<\/p>\n<p>Once you have the options, it is time to generate the source files:<\/p>\n<div class=\"highlight highlight-source-cs\">\n<pre><span class=\"pl-k\">static<\/span> <span class=\"pl-k\">string<\/span> <span class=\"pl-en\">SourceFileFromMustachePath<\/span>(<span class=\"pl-k\">string<\/span> <span class=\"pl-smi\">name<\/span>, <span class=\"pl-k\">string<\/span> <span class=\"pl-smi\">template<\/span>, <span class=\"pl-k\">string<\/span> <span class=\"pl-smi\">hash<\/span>)\r\n{\r\n    <span class=\"pl-en\">Func<\/span>&lt;<span class=\"pl-k\">object<\/span>, <span class=\"pl-k\">string<\/span>&gt; <span class=\"pl-smi\">tree<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-smi\">HandlebarsDotNet<\/span>.<span class=\"pl-smi\">Handlebars<\/span>.<span class=\"pl-en\">Compile<\/span>(<span class=\"pl-smi\">template<\/span>);\r\n    <span class=\"pl-k\">object<\/span> <span class=\"pl-smi\">@object<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-smi\">Newtonsoft<\/span>.<span class=\"pl-smi\">Json<\/span>.<span class=\"pl-smi\">JsonConvert<\/span>.<span class=\"pl-en\">DeserializeObject<\/span>(<span class=\"pl-smi\">hash<\/span>);\r\n    <span class=\"pl-k\">string<\/span> <span class=\"pl-smi\">mustacheText<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-en\">tree<\/span>(<span class=\"pl-smi\">@object<\/span>);\r\n\r\n    <span class=\"pl-k\">return<\/span> <span class=\"pl-en\">GenerateMustacheClass<\/span>(<span class=\"pl-smi\">name<\/span>, <span class=\"pl-smi\">mustacheText<\/span>);\r\n}<\/pre>\n<\/div>\n<p>First we use <a href=\"https:\/\/github.com\/rexm\/Handlebars.Net\">Handlebars.net<\/a> to create the string constant text (first 3 lines above).\nWe then move on to the task of generating the property to contain it.<\/p>\n<div class=\"highlight highlight-source-cs\">\n<pre><span class=\"pl-k\">private<\/span> <span class=\"pl-k\">static<\/span> <span class=\"pl-k\">string<\/span> <span class=\"pl-en\">GenerateMustacheClass<\/span>(<span class=\"pl-k\">string<\/span> <span class=\"pl-smi\">className<\/span>, <span class=\"pl-k\">string<\/span> <span class=\"pl-smi\">mustacheText<\/span>)\r\n{\r\n    <span class=\"pl-en\">StringBuilder<\/span> <span class=\"pl-smi\">sb<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-k\">new<\/span> <span class=\"pl-en\">StringBuilder<\/span>();\r\n    <span class=\"pl-smi\">sb<\/span>.<span class=\"pl-en\">Append<\/span>(<span class=\"pl-s\"><span class=\"pl-pds\">$@\"<\/span><\/span>\r\n<span class=\"pl-s\">namespace Mustache {{<\/span>\r\n\r\n<span class=\"pl-s\">public static partial class Constants {{<\/span>\r\n\r\n<span class=\"pl-s\">public const string {<span class=\"pl-smi\">className<\/span>} = @<span class=\"pl-cce\">\"\"<\/span>{<span class=\"pl-smi\">mustacheText<\/span>.<span class=\"pl-en\">Replace<\/span>(<span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span><span class=\"pl-cce\">\\\"<\/span><span class=\"pl-pds\">\"<\/span><\/span>, <span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span><span class=\"pl-cce\">\\\"\\\"<\/span><span class=\"pl-pds\">\"<\/span><\/span>)}<span class=\"pl-cce\">\"\"<\/span>;<\/span>\r\n<span class=\"pl-s\">}}<\/span>\r\n<span class=\"pl-s\">}}<\/span>\r\n<span class=\"pl-s\"><span class=\"pl-pds\">\"<\/span><\/span>);\r\n    <span class=\"pl-k\">return<\/span> <span class=\"pl-smi\">sb<\/span>.<span class=\"pl-en\">ToString<\/span>();\r\n\r\n}<\/pre>\n<\/div>\n<p>That was easy, mainly thanks to C# partial classes. We generate a single class from multiple source files.<\/p>\n<h2><a id=\"user-content-conclusion\" class=\"anchor\" href=\"#conclusion\" aria-hidden=\"true\"><\/a>Conclusion<\/h2>\n<p>C# Source Generators are a great addition to the compiler. The ability to interject yourself in the middle of the compilation process and have access to the source tree, makes it possible, even simple, to enable all sorts of scenarios (i.e. domain languages, code interpolation, automatic optimizations &#8230;). We look forward to you surprising us with your own Source Generators!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This post describes two new generators that we added to the samples project in the Roslyn SDK github repo.<\/p>\n<p>The first generator gives you strongly typed access to CSV data. The second one creates string constants based on Mustache specifications.<\/p>\n","protected":false},"author":34619,"featured_media":58792,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,196,195,756],"tags":[],"class_list":["post-29404","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-dotnet-core","category-dotnet-framework","category-csharp"],"acf":[],"blog_post_summary":"<p>This post describes two new generators that we added to the samples project in the Roslyn SDK github repo.<\/p>\n<p>The first generator gives you strongly typed access to CSV data. The second one creates string constants based on Mustache specifications.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/29404","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/34619"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=29404"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/29404\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/58792"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=29404"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=29404"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=29404"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}