Defines can be added for projects or individual files. I propose the ability to have the same source file included multiple times with different defines being present/absent for each parsing.
The main reason why this would be desirable is to have the ability to provide async support without having to write the common parts of the method/class twice. Using partial classes one could simple use the preprocessor directives to provide a different method signature/head and call the corresponding await/async methods when necessary.
Here a quick example of what this would look like.
// class needs to be partial
partial class StreamHelper
{
#if Async
public static async IAsyncEnumerable<T> ParseLinesAsync<T>(Stream stream, IStringParser<T> parser, [EnumeratorCancellation] CancellationToken cancellationToken)
#else
public static IEnumerable<T> ParseLines<T>(Stream stream, IStringParser<T> parser)
#endif
{
using StreamReader sr = new StreamReader(stream, leaveOpen: true);
string? line;
while (true)
{
#if Async
line = await sr.ReadLineAsync(cancellationToken).ConfigureAwait(false);
#else
line = sr.ReadLine();
#endif
// break at end-of-stream
if (line is null)
break;
// line comment starts with #
if (line.SkipWhile(c => char.IsWhiteSpace(c)).FirstOrDefault() is '#')
continue;
yield return parser.Parse(line);
}
}
}
While async is the most common example one would think of, it could also be used in methods that require 99% the same code but only differ somewhere in the middle, with no convenient way to just repurpose the code. I believe, at the moment, this is usually done by adding a private _Internal method with a delegate to handle the task specific situations.
public void DoThingA(object param1, object param2) => DoThingCommonInternal(param1, param2, DoThingAInternal);
public void DoThingB(object param1, object param2) => DoThingCommonInternal(param1, param2, DoThingBInternal);
private void DoThingCommonInternal(object param1, object param2, Action handler)
{
// common part for A and B
handler();
// common part for A and B
}
private void DoThingAInternal() { /* Handler for A specific things */ }
private void DoThingBInternal() { /* Handler for B specific things */ }
When it could be shrunken down to this:
#if THING_A
public void DoThingA(object param1, object param2)
#else
public void DoThingB(object param1, object param2)
#endif
{
// common part for A and B
#if THING_A
{ /* Handler for A specific things */ }
#else
{ /* Handler for B specific things */ }
#endif
// common part for A and B
}
Admittedly, whether this is better or worse depends a lot on how different ThingA and ThingB here really are.
A feature like this could be provided either on the necessary file itself, similar to how defines are provided now. Like #multipass Async. Or through the csproj file. Ideally though, the IDE would have to show all different renditions of the file with the different defines being active:
For example: Having #multipass Async;Custom on the file StreamHelper.cs could show up as:
@JackTheSpades side note: fyi if you need to have async and sync versions of the same method, this can be easily achieved with this source generator. No need to wait new Roslyn stuff.
Defines can be added for projects or individual files. I propose the ability to have the same source file included multiple times with different defines being present/absent for each parsing.
The main reason why this would be desirable is to have the ability to provide
async
support without having to write the common parts of the method/class twice. Using partial classes one could simple use the preprocessor directives to provide a different method signature/head and call the correspondingawait
/async
methods when necessary.Here a quick example of what this would look like.
While async is the most common example one would think of, it could also be used in methods that require 99% the same code but only differ somewhere in the middle, with no convenient way to just repurpose the code. I believe, at the moment, this is usually done by adding a private
_Internal
method with a delegate to handle the task specific situations.When it could be shrunken down to this:
Admittedly, whether this is better or worse depends a lot on how different
ThingA
andThingB
here really are.A feature like this could be provided either on the necessary file itself, similar to how defines are provided now. Like
#multipass Async
. Or through the csproj file. Ideally though, the IDE would have to show all different renditions of the file with the different defines being active:For example: Having
#multipass Async;Custom
on the fileStreamHelper.cs
could show up as: