RSCG – Meziantou.Polyfill
| name | Meziantou.Polyfill |
| nuget | https://www.nuget.org/packages/Meziantou.Polyfill/ |
| link | https://www.meziantou.net/polyfills-in-dotnet-to-ease-multi-targeting.htm |
| author | Gérald Barré |
Generating polyfills that you can see source without de-compiling
This is how you can use Meziantou.Polyfill .
The code that you start with is
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net7.0;netstandard2.0</TargetFrameworks>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Meziantou.Polyfill" Version="1.0.28">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
</Project>
The code that you will use is
// See https://aka.ms/new-console-template for more information System.Console.WriteLine("Hello,World!");
using System.Diagnostics;
using System.Threading.Tasks;
namespace Meziantou.PolyfillDemo
{
internal class StartProcess
{
static async Task StartNotepad()
{
await Task.Delay(1000);
var process = Process.Start("notepad.exe");
#if NET6_0_OR_GREATER
await process.WaitForExitAsync();
#else
process.WaitForExit();
#endif
}
static async Task StartNotepadPolyFill()
{
await Task.Delay(1000);
var process = Process.Start("notepad.exe");
//do remove nuget Meziantou.Polyfill - this line will not be ok.
await process.WaitForExitAsync();
}
}
}
The code that is generated is
// IncludedMembers: <null>
// ExcludedMembers: <null>
// System.Collections.Immutable.ImmutableArray`1: True
// System.Memory`1: True
// System.Net.Http.HttpContent: True
// System.ReadOnlyMemory`1: True
// System.ReadOnlySpan`1: True
// System.Span`1: True
// System.Threading.Tasks.ValueTask: True
// System.Threading.Tasks.ValueTask`1: True
//
// M:System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd``1(`0,System.Func{`0,``0,`1},``0): False
// M:System.Collections.Generic.CollectionExtensions.GetValueOrDefault``2(System.Collections.Generic.IReadOnlyDictionary{``0,``1},``0): False
// M:System.Collections.Generic.CollectionExtensions.GetValueOrDefault``2(System.Collections.Generic.IReadOnlyDictionary{``0,``1},``0,``1): False
// M:System.Collections.Generic.KeyValuePair`2.Deconstruct(`0@,`1@): False
// M:System.Collections.Generic.Queue`1.TryDequeue(`0@): False
// M:System.Collections.Immutable.ImmutableArray`1.AsSpan(System.Int32,System.Int32): False
// M:System.Collections.Immutable.ImmutableArray`1.AsSpan(System.Range): False
// M:System.Diagnostics.Process.WaitForExitAsync(System.Threading.CancellationToken): False
// M:System.IO.Stream.Read(System.Span{System.Byte}): False
// M:System.IO.Stream.ReadAsync(System.Memory{System.Byte},System.Threading.CancellationToken): False
// M:System.IO.Stream.ReadAtLeast(System.Span{System.Byte},System.Int32,System.Boolean): False
// M:System.IO.Stream.ReadAtLeastAsync(System.Memory{System.Byte},System.Int32,System.Boolean,System.Threading.CancellationToken): False
// M:System.IO.Stream.Write(System.ReadOnlySpan{System.Byte}): False
// M:System.IO.Stream.WriteAsync(System.ReadOnlyMemory{System.Byte},System.Threading.CancellationToken): False
// M:System.IO.StreamReader.ReadLineAsync(): False
// M:System.IO.StreamReader.ReadLineAsync(System.Threading.CancellationToken): False
// M:System.IO.TextReader.ReadAsync(System.Memory{System.Char},System.Threading.CancellationToken): False
// M:System.IO.TextReader.ReadToEndAsync(System.Threading.CancellationToken): False
// M:System.IO.TextWriter.WriteAsync(System.ReadOnlyMemory{System.Char},System.Threading.CancellationToken): False
// M:System.Linq.Enumerable.DistinctBy``2(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1}): False
// M:System.Linq.Enumerable.DistinctBy``2(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1},System.Collections.Generic.IEqualityComparer{``1}): False
// M:System.Linq.Enumerable.MaxBy``2(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1}): False
// M:System.Linq.Enumerable.MaxBy``2(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1},System.Collections.Generic.IComparer{``1}): False
// M:System.Linq.Enumerable.MinBy``2(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1}): False
// M:System.Linq.Enumerable.MinBy``2(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1},System.Collections.Generic.IComparer{``1}): False
// M:System.Linq.Enumerable.OrderDescending``1(System.Collections.Generic.IEnumerable{``0}): False
// M:System.Linq.Enumerable.OrderDescending``1(System.Collections.Generic.IEnumerable{``0},System.Collections.Generic.IComparer{``0}): False
// M:System.Linq.Enumerable.Order``1(System.Collections.Generic.IEnumerable{``0}): False
// M:System.Linq.Enumerable.Order``1(System.Collections.Generic.IEnumerable{``0},System.Collections.Generic.IComparer{``0}): False
// M:System.Linq.Enumerable.ToHashSet``1(System.Collections.Generic.IEnumerable{``0}): False
// M:System.Linq.Enumerable.ToHashSet``1(System.Collections.Generic.IEnumerable{``0},System.Collections.Generic.IEqualityComparer{``0}): False
// M:System.Linq.Enumerable.Zip``2(System.Collections.Generic.IEnumerable{``0},System.Collections.Generic.IEnumerable{``1}): False
// M:System.MemoryExtensions.AsSpan(System.String,System.Int32,System.Int32): False
// M:System.MemoryExtensions.Contains``1(System.ReadOnlySpan{``0},``0): False
// M:System.MemoryExtensions.Contains``1(System.Span{``0},``0): False
// M:System.Net.Http.HttpContent.CopyTo(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken): False
// M:System.Net.Http.HttpContent.CopyToAsync(System.IO.Stream): False
// M:System.Net.Http.HttpContent.CopyToAsync(System.IO.Stream,System.Net.TransportContext): False
// M:System.Net.Http.HttpContent.CopyToAsync(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken): False
// M:System.Net.Http.HttpContent.CopyToAsync(System.IO.Stream,System.Threading.CancellationToken): False
// M:System.Net.Http.HttpContent.ReadAsStream(System.Threading.CancellationToken): False
// M:System.Net.Http.HttpContent.ReadAsStream: False
// M:System.String.Contains(System.Char): False
// M:System.String.Contains(System.Char,System.StringComparison): False
// M:System.String.Contains(System.String,System.StringComparison): False
// M:System.String.CopyTo(System.Span{System.Char}): False
// M:System.String.EndsWith(System.Char): False
// M:System.String.GetHashCode(System.StringComparison): False
// M:System.String.IndexOf(System.Char,System.StringComparison): False
// M:System.String.Replace(System.String,System.String,System.StringComparison): False
// M:System.String.ReplaceLineEndings(System.String): False
// M:System.String.ReplaceLineEndings: False
// M:System.String.Split(System.Char,System.Int32,System.StringSplitOptions): False
// M:System.String.Split(System.Char,System.StringSplitOptions): False
// M:System.String.StartsWith(System.Char): False
// M:System.String.TryCopyTo(System.Span{System.Char}): False
// M:System.Text.Encoding.GetString(System.ReadOnlySpan{System.Byte}): False
// M:System.Text.StringBuilder.Append(System.ReadOnlyMemory{System.Char}): False
// M:System.Text.StringBuilder.Append(System.ReadOnlySpan{System.Char}): False
// M:System.Text.StringBuilder.AppendJoin(System.Char,System.Object[]): False
// M:System.Text.StringBuilder.AppendJoin(System.Char,System.String[]): False
// M:System.Text.StringBuilder.AppendJoin(System.String,System.Object[]): False
// M:System.Text.StringBuilder.AppendJoin(System.String,System.String[]): False
// M:System.Text.StringBuilder.AppendJoin``1(System.Char,System.Collections.Generic.IEnumerable{``0}): False
// M:System.Text.StringBuilder.AppendJoin``1(System.String,System.Collections.Generic.IEnumerable{``0}): False
// M:System.Threading.CancellationTokenSource.CancelAsync: True
// M:System.Threading.Tasks.Task.WaitAsync(System.Threading.CancellationToken): False
// T:System.Collections.Generic.PriorityQueue`2: False
// T:System.Collections.Generic.ReferenceEqualityComparer: False
// T:System.Diagnostics.CodeAnalysis.AllowNullAttribute: False
// T:System.Diagnostics.CodeAnalysis.DisallowNullAttribute: False
// T:System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute: False
// T:System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute: False
// T:System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute: False
// T:System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes: False
// T:System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute: False
// T:System.Diagnostics.CodeAnalysis.MaybeNullAttribute: False
// T:System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute: False
// T:System.Diagnostics.CodeAnalysis.MemberNotNullAttribute: False
// T:System.Diagnostics.CodeAnalysis.MemberNotNullWhenAttribute: False
// T:System.Diagnostics.CodeAnalysis.NotNullAttribute: False
// T:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute: False
// T:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute: False
// T:System.Diagnostics.CodeAnalysis.RequiresAssemblyFilesAttribute: False
// T:System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute: False
// T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute: False
// T:System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute: False
// T:System.Diagnostics.CodeAnalysis.StringSyntaxAttribute: False
// T:System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute: False
// T:System.Diagnostics.CodeAnalysis.UnscopedRefAttribute: False
// T:System.Diagnostics.StackTraceHiddenAttribute: False
// T:System.HashCode: False
// T:System.Index: False
// T:System.Net.Http.ReadOnlyMemoryContent: False
// T:System.Range: False
// T:System.Runtime.CompilerServices.AsyncMethodBuilderAttribute: False
// T:System.Runtime.CompilerServices.CallerArgumentExpressionAttribute: False
// T:System.Runtime.CompilerServices.CollectionBuilderAttribute: True
// T:System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute: False
// T:System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute: False
// T:System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute: False
// T:System.Runtime.CompilerServices.InterpolatedStringHandlerAttribute: False
// T:System.Runtime.CompilerServices.IsExternalInit: False
// T:System.Runtime.CompilerServices.ModuleInitializerAttribute: False
// T:System.Runtime.CompilerServices.RequiredMemberAttribute: False
// T:System.Runtime.CompilerServices.SkipLocalsInitAttribute: False
// T:System.Runtime.CompilerServices.TupleElementNamesAttribute: False
// T:System.Runtime.InteropServices.SuppressGCTransitionAttribute: False
// T:System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute: False
// T:System.Runtime.Versioning.ObsoletedOSPlatformAttribute: False
// T:System.Runtime.Versioning.RequiresPreviewFeaturesAttribute: False
// T:System.Runtime.Versioning.SupportedOSPlatformAttribute: False
// T:System.Runtime.Versioning.SupportedOSPlatformGuardAttribute: False
// T:System.Runtime.Versioning.TargetPlatformAttribute: False
// T:System.Runtime.Versioning.UnsupportedOSPlatformAttribute: False
// T:System.Runtime.Versioning.UnsupportedOSPlatformGuardAttribute: False
// T:System.Threading.Tasks.TaskToAsyncResult: True
// T:System.ValueTuple: False
// T:System.ValueTuple`1: False
// T:System.ValueTuple`2: False
// T:System.ValueTuple`3: False
// T:System.ValueTuple`4: False
// T:System.ValueTuple`5: False
// T:System.ValueTuple`6: False
// T:System.ValueTuple`7: False
// T:System.ValueTuple`8: False
// T:System.ITupleInternal: False
// <auto-generated/>
#pragma warning disable
#nullable enable annotations
using System.Threading.Tasks;
using System.Threading;
using System.Diagnostics;
using System;
static partial class PolyfillExtensions
{
public static async Task WaitForExitAsync(this Process target,CancellationToken cancellationToken = default)
{
// https://source.dot.net/#System.Diagnostics.Process/System/Diagnostics/Process.cs,b6a5b00714a61f06
// Because the process has already started by the time this method is called,
// we're in a race against the process to set up our exit handlers before the process
// exits. As a result,there are several different flows that must be handled:
//
// CASE 1: WE ENABLE EVENTS
// This is the "happy path". In this case we enable events.
//
// CASE 1.1: PROCESS EXITS OR IS CANCELED AFTER REGISTERING HANDLER
// This case continues the "happy path". The process exits or waiting is canceled after
// registering the handler and no special cases are needed.
//
// CASE 1.2: PROCESS EXITS BEFORE REGISTERING HANDLER
// It's possible that the process can exit after we enable events but before we reigster
// the handler. In that case we must check for exit after registering the handler.
//
//
// CASE 2: PROCESS EXITS BEFORE ENABLING EVENTS
// The process may exit before we attempt to enable events. In that case EnableRaisingEvents
// will throw an exception like this:
// System.InvalidOperationException : Cannot process request because the process (42) has exited.
// In this case we catch the InvalidOperationException. If the process has exited,our work
// is done and we return. If for any reason (now or in the future) enabling events fails
// and the process has not exited,bubble the exception up to the user.
//
//
// CASE 3: USER ALREADY ENABLED EVENTS
// In this case the user has already enabled raising events. Re-enabling events is a no-op
// as the value hasn't changed. However,no-op also means that if the process has already
// exited,EnableRaisingEvents won't throw an exception.
//
// CASE 3.1: PROCESS EXITS OR IS CANCELED AFTER REGISTERING HANDLER
// (See CASE 1.1)
//
// CASE 3.2: PROCESS EXITS BEFORE REGISTERING HANDLER
// (See CASE 1.2)
if (!target.HasExited)
{
// Early out for cancellation before doing more expensive work
cancellationToken.ThrowIfCancellationRequested();
}
try
{
// CASE 1: We enable events
// CASE 2: Process exits before enabling events (and throws an exception)
// CASE 3: User already enabled events (no-op)
target.EnableRaisingEvents = true;
}
catch (InvalidOperationException)
{
// CASE 2: If the process has exited,our work is done,otherwise bubble the
// exception up to the user
if (target.HasExited)
{
return;
}
throw;
}
var tcs = new TaskCompletionSourceWithCancellation<bool>();
void Handler(object? s,EventArgs e) => tcs.TrySetResult(true);
target.Exited += Handler;
try
{
if (target.HasExited)
{
// CASE 1.2 & CASE 3.2: Handle race where the process exits before registering the handler
return;
}
// CASE 1.1 & CASE 3.1: Process exits or is canceled here
await tcs.WaitWithCancellationAsync(cancellationToken).ConfigureAwait(false);
}
finally
{
target.Exited -= Handler;
}
target.WaitForExit();
}
private sealed class TaskCompletionSourceWithCancellation<T> : TaskCompletionSource<T>
{
private CancellationToken _cancellationToken;
public TaskCompletionSourceWithCancellation() : base(TaskCreationOptions.RunContinuationsAsynchronously)
{
}
private void OnCancellation()
{
TrySetCanceled(_cancellationToken);
}
#if NETCOREAPP3_1_OR_GREATER
public async ValueTask<T> WaitWithCancellationAsync(CancellationToken cancellationToken)
{
_cancellationToken = cancellationToken;
await using (cancellationToken.UnsafeRegister(s => ((TaskCompletionSourceWithCancellation<T>)s!).OnCancellation(),this))
{
return await Task.ConfigureAwait(false);
}
}
#else
public async Task<T> WaitWithCancellationAsync(CancellationToken cancellationToken)
{
_cancellationToken = cancellationToken;
using (cancellationToken.Register(s => ((TaskCompletionSourceWithCancellation<T>)s!).OnCancellation(),this))
{
return await Task.ConfigureAwait(false);
}
}
#endif
}
}
Code and pdf at
https://ignatandrei.github.io/RSCG_Examples/v2/docs/Meziantou.Polyfill
Leave a Reply