RSCG – Meziantou.Polyfill
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