File to CSharp Const
I was getting tired how many times I am reading from embedded resources – and finding it over and over the same code. So – what if I will transform the file into C# text ? Seems a work for Roslyn Source Code Generator – and the new one
IIncrementalGenerator
https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.md , not the old ISourceGenerator https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview
So the code reads from additional files defined in .csproj
<ItemGroup>
<AdditionalFiles Include=”Second.gen.txt” />
<AdditionalFiles Include=”first.gen.txt” />
<AdditionalFiles Include=”test\Afirst.gen.txt” />
< /ItemGroup>
and then transforms in a const:
string x= MyAdditionalFiles.Second_gen_txt;
Now, how difficult is the code?
The first version was
public void Initialize(IncrementalGeneratorInitializationContext initContext) { IncrementalValuesProvider<AdditionalText> textFiles = initContext .AdditionalTextsProvider .Where(file => file.Path.Contains(".gen.")); // read their contents and save their name IncrementalValuesProvider<(string name, string content)> namesAndContents = textFiles .Select((text, cancellationToken) => (name: Path.GetFileName(text.Path), content: text.GetText(cancellationToken)!.ToString())); // generate a class that contains their values as const strings initContext.RegisterSourceOutput(namesAndContents, (spc, nameAndContent) => { string nameString=nameAndContent.name .Replace(".","_") .Replace("-", "_") ; string[] lines = nameAndContent.content .Split('\n', '\r') .Where(it => it.Trim().Length > 0) .Select(it => it.Replace("\\", "\\\\")) .Select(it => it.Replace("\"", "\\\"")) .Select(it => "\"" + it + "\\r\\n" + "\"") .ToArray(); string content = string.Join("\r\n+", lines); string hint = $"MyAdditionalFiles.{nameAndContent.name}"; var str = $@" public static partial class MyAdditionalFiles {{ //https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/raw-string public const string {nameString} = {content}; }}"; spc.AddSource(hint,str ); }); }
But it have performed poorly ( 3 minutes build for a file with 2MB ) . I was thinking that splitting into lines and replacing quotes and / was the faulty
So raw strings, https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/raw-string , to the help – the second version is
//v1 //string[] lines = nameAndContent.content // .Split('\n', '\r') // .Where(it => it.Trim().Length > 0) // .Select(it => it.Replace("\\","\\\\")) // .Select(it=>it.Replace("\"","\\\"")) // .Select(it => "\"" + it +"\\r\\n"+ "\"") // .ToArray(); //string content = string.Join("\r\n+",lines); //v2 => .net 7 string content=nameAndContent.content; //too much, but otherwise start counting quotes var quotes = new string('"', 10); content = quotes+"\r\n"+ content+"\r\n"+ quotes;
Additional details to be mentioned:
1.The Release with GitHUb Actions should also to be mentioned –the deployment to NUGET is a easy way now
2. Debugging is relative easy https://github.com/JoanComasFdz/dotnet-how-to-debug-source-generator-vs2022
3. It is a day of work to get rid of embedded files!
4. Does not work with binary files ( error with AdditionalFiles )
You can find the package at https://github.com/ignatandrei/RSCG_Utils .
Leave a Reply