diff --git a/apiCount.include.md b/apiCount.include.md index 9f9aacf2..2272c410 100644 --- a/apiCount.include.md +++ b/apiCount.include.md @@ -1,28 +1,28 @@ -**API count: 1091** +**API count: 1095** ### Per Target Framework | Target | APIs | | -- | -- | -| `net461` | 1022 | -| `net462` | 1022 | -| `net47` | 1021 | -| `net471` | 1020 | -| `net472` | 1016 | -| `net48` | 1016 | -| `net481` | 1016 | -| `netstandard2.0` | 1018 | -| `netstandard2.1` | 871 | -| `netcoreapp2.0` | 941 | -| `netcoreapp2.1` | 882 | -| `netcoreapp2.2` | 882 | -| `netcoreapp3.0` | 843 | -| `netcoreapp3.1` | 842 | -| `net5.0` | 714 | -| `net6.0` | 615 | -| `net7.0` | 462 | -| `net8.0` | 341 | +| `net461` | 1026 | +| `net462` | 1026 | +| `net47` | 1025 | +| `net471` | 1024 | +| `net472` | 1020 | +| `net48` | 1020 | +| `net481` | 1020 | +| `netstandard2.0` | 1022 | +| `netstandard2.1` | 875 | +| `netcoreapp2.0` | 945 | +| `netcoreapp2.1` | 886 | +| `netcoreapp2.2` | 886 | +| `netcoreapp3.0` | 847 | +| `netcoreapp3.1` | 846 | +| `net5.0` | 718 | +| `net6.0` | 619 | +| `net7.0` | 466 | +| `net8.0` | 345 | | `net9.0` | 232 | | `net10.0` | 178 | | `net11.0` | 58 | -| `uap10.0` | 1008 | +| `uap10.0` | 1012 | diff --git a/api_list.include.md b/api_list.include.md index d4cb1652..776189f0 100644 --- a/api_list.include.md +++ b/api_list.include.md @@ -151,10 +151,8 @@ * `bool IsBetween(char, char, char)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.char.isbetween?view=net-11.0) * `char ToLowerOrdinal(char)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.char.tolowerordinal?view=net-11.0) * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. - * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. * `char ToUpperOrdinal(char)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.char.toupperordinal?view=net-11.0) * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. - * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. #### ConcurrentBag @@ -176,7 +174,6 @@ * `bool Remove(TKey, TValue) where TKey : class where TValue : class` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.conditionalweaktable-2.remove?view=net-11.0#system-runtime-compilerservices-conditionalweaktable-2-remove(-0-1@)) * Note: Lookup and removal are not performed under the table lock, so the operation is not atomic with regard to concurrent mutations. - * Note: Lookup and removal are not performed under the table lock, so the operation is not atomic with regard to concurrent mutations. #### Console @@ -277,21 +274,15 @@ * `void EnsureCapacity(int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2.ensurecapacity?view=net-11.0) * Note: No-op on older targets; the BCL grows the backing storage. - * Note: No-op on older targets; the BCL grows the backing storage. * `DictionaryAlternateLookup GetAlternateLookup() where TKey : notnull` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2.getalternatelookup?view=net-11.0) * Note: Lookups are O(n) on older targets; the BCL is O(1). * Note: Returns the free-standing `DictionaryAlternateLookup` rather than the BCL's nested `Dictionary.AlternateLookup`. Use `var` for cross-target code. - * Note: Lookups are O(n) on older targets; the BCL is O(1). - * Note: Returns the free-standing `DictionaryAlternateLookup` rather than the BCL's nested `Dictionary.AlternateLookup`. Use `var` for cross-target code. * `void TrimExcess(int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2.trimexcess?view=net-11.0#system-collections-generic-dictionary-2-trimexcess(system-int32)) * Note: No-op on older targets; the BCL shrinks the backing storage. - * Note: No-op on older targets; the BCL shrinks the backing storage. * `void TrimExcess()` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2.trimexcess?view=net-11.0#system-collections-generic-dictionary-2-trimexcess) * Note: No-op on older targets; the BCL shrinks the backing storage. - * Note: No-op on older targets; the BCL shrinks the backing storage. * `bool TryGetAlternateLookup(DictionaryAlternateLookup) where TKey : notnull` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2.trygetalternatelookup?view=net-11.0) * Note: Lookups are O(n) on older targets; the BCL is O(1). - * Note: Lookups are O(n) on older targets; the BCL is O(1). #### DictionaryEntry @@ -350,7 +341,6 @@ * `Encoding Latin1` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.encoding.latin1?view=net-11.0) * `ReadOnlySpan Preamble` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.encoding.preamble?view=net-11.0) * Note: Allocates a new array on each access, unlike the BCL property which returns a cached span. - * Note: Allocates a new array on each access, unlike the BCL property which returns a cached span. #### Enum @@ -475,18 +465,13 @@ * `void EnsureCapacity(int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.hashset-1.ensurecapacity?view=net-11.0#system-collections-generic-hashset-1-ensurecapacity(system-int32)) * Note: No-op on older targets; the BCL grows the backing storage. - * Note: No-op on older targets; the BCL grows the backing storage. * `HashSetAlternateLookup GetAlternateLookup()` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.hashset-1.getalternatelookup?view=net-11.0) * Note: Lookups are O(n) on older targets; the BCL is O(1). * Note: Returns the free-standing `HashSetAlternateLookup` rather than the BCL's nested `HashSet.AlternateLookup`. Use `var` for cross-target code. - * Note: Lookups are O(n) on older targets; the BCL is O(1). - * Note: Returns the free-standing `HashSetAlternateLookup` rather than the BCL's nested `HashSet.AlternateLookup`. Use `var` for cross-target code. * `void TrimExcess(int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.hashset-1.trimexcess?view=net-11.0#system-collections-generic-hashset-1-trimexcess(system-int32)) * Note: No-op on older targets; the BCL shrinks the backing storage. - * Note: No-op on older targets; the BCL shrinks the backing storage. * `bool TryGetAlternateLookup(HashSetAlternateLookup)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.hashset-1.trygetalternatelookup?view=net-11.0) * Note: Lookups are O(n) on older targets; the BCL is O(1). - * Note: Lookups are O(n) on older targets; the BCL is O(1). * `bool TryGetValue(T, T)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.hashset-1.trygetvalue?view=net-11.0) @@ -704,12 +689,9 @@ * `void EnsureCapacity(int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.ensurecapacity?view=net-11.0#system-collections-generic-list-1-ensurecapacity(system-int32)) * Note: No-op on older targets; the BCL grows the backing storage. * Note: Returns void on older targets; the BCL returns int (the new capacity). - * Note: No-op on older targets; the BCL grows the backing storage. - * Note: Returns void on older targets; the BCL returns int (the new capacity). * `void InsertRange(int, ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.collectionextensions.insertrange?view=net-11.0) * `void TrimExcess()` * Note: No-op on older targets; the BCL shrinks the backing storage. - * Note: No-op on older targets; the BCL shrinks the backing storage. #### Math @@ -841,7 +823,6 @@ * `Task<(byte[] StandardOutput, byte[] StandardError)> ReadAllBytesAsync(CancellationToken)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.readallbytesasync?view=net-11.0) * `IEnumerable ReadAllLines(TimeSpan?)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.readalllines?view=net-11.0) * Note: The timeout is measured from the point enumeration starts, since the returned sequence is lazy. - * Note: The timeout is measured from the point enumeration starts, since the returned sequence is lazy. * `IAsyncEnumerable ReadAllLinesAsync(CancellationToken)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.readalllinesasync?view=net-11.0) * `(string StandardOutput, string StandardError) ReadAllText(TimeSpan?)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.readalltext?view=net-11.0) * `Task<(string StandardOutput, string StandardError)> ReadAllTextAsync(CancellationToken)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.readalltextasync?view=net-11.0) @@ -886,10 +867,8 @@ * `void EnsureCapacity(int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.queue-1.ensurecapacity?view=net-11.0#system-collections-generic-queue-1-ensurecapacity(system-int32)) * Note: No-op on older targets; the BCL grows the backing storage. - * Note: No-op on older targets; the BCL grows the backing storage. * `void TrimExcess(int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.queue-1.trimexcess?view=net-11.0#system-collections-generic-queue-1-trimexcess(system-int32)) * Note: No-op on older targets; the BCL shrinks the backing storage. - * Note: No-op on older targets; the BCL shrinks the backing storage. * `bool TryDequeue(T)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.queue-1.trydequeue?view=net-11.0) * `bool TryPeek(T)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.queue-1.trypeek?view=net-11.0) @@ -945,10 +924,8 @@ * `bool StartsWith(string, StringComparison)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.memoryextensions.startswith?view=net-11.0#system-memoryextensions-startswith-1(system-readonlyspan((-0))-system-readonlyspan((-0)))) * `int ToLowerOrdinal(Span)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.memoryextensions.tolowerordinal?view=net-11.0) * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. - * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. * `int ToUpperOrdinal(Span)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.memoryextensions.toupperordinal?view=net-11.0) * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. - * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. * `bool TryNormalize(Span, int, NormalizationForm)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.stringnormalizationextensions.trynormalize?view=net-11.0#system-stringnormalizationextensions-trynormalize(system-readonlyspan((system-char))-system-span((system-char))-system-int32@-system-text-normalizationform)) @@ -1023,10 +1000,8 @@ * `bool Equals(Rune, StringComparison)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.rune.equals?view=net-11.0#system-text-rune-equals(system-text-rune-system-stringcomparison)) * `Rune ToLowerOrdinal(Rune)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.rune.tolowerordinal?view=net-11.0) * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. - * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. * `Rune ToUpperOrdinal(Rune)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.rune.toupperordinal?view=net-11.0) * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. - * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. #### RuntimeFeature @@ -1167,10 +1142,8 @@ * `void EnsureCapacity(int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.stack-1.ensurecapacity?view=net-11.0) * Note: No-op on older targets; the BCL grows the backing storage. - * Note: No-op on older targets; the BCL grows the backing storage. * `void TrimExcess(int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.stack-1.trimexcess?view=net-11.0#system-collections-generic-stack-1-trimexcess(system-int32)) * Note: No-op on older targets; the BCL shrinks the backing storage. - * Note: No-op on older targets; the BCL shrinks the backing storage. * `bool TryPeek(T)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.stack-1.trypeek?view=net-11.0) * `bool TryPop(T)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.stack-1.trypop?view=net-11.0) @@ -1222,10 +1195,8 @@ * `bool StartsWith(char)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.string.startswith?view=net-11.0#system-string-startswith(system-char)) * `string ToLowerOrdinal()` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.string.tolowerordinal?view=net-11.0) * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. - * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. * `string ToUpperOrdinal()` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.string.toupperordinal?view=net-11.0) * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. - * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. * `bool TryCopyTo(Span)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.string.trycopyto?view=net-11.0) * `string Concat(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.string.concat?view=net-11.0#system-string-concat(system-readonlyspan((system-object)))) * `string Concat(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.string.concat?view=net-11.0#system-string-concat(system-readonlyspan((system-string)))) @@ -1263,7 +1234,6 @@ * `void CopyTo(int, Span, int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.copyto?view=net-11.0#system-text-stringbuilder-copyto(system-int32-system-span((system-char))-system-int32)) * `StringBuilderRuneEnumerator EnumerateRunes()` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.enumeraterunes?view=net-11.0) * Note: Enumerates over a snapshot of the content taken when enumeration starts, so changes made to the StringBuilder while enumerating are not observed. - * Note: Enumerates over a snapshot of the content taken when enumeration starts, so changes made to the StringBuilder while enumerating are not observed. * `bool Equals(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.equals?view=net-11.0#system-text-stringbuilder-equals(system-readonlyspan((system-char)))) * `ChunkEnumerator GetChunks()` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.getchunks?view=net-11.0) * `Rune GetRuneAt(int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.getruneat?view=net-11.0) @@ -1296,9 +1266,21 @@ * `Task WaitAsync(TimeSpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task-1.waitasync?view=net-11.0#system-threading-tasks-task-1-waitasync(system-timespan)) +#### TaskCompletionSource + + * `void SetFromTask(Task)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcompletionsource.setfromtask?view=net-11.0) + * Note: When the supplied task is canceled, the canceling token is recovered by observing that task, which costs a throw and catch that the BCL avoids. + * `bool TrySetFromTask(Task)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcompletionsource.trysetfromtask?view=net-11.0) + * Note: When the supplied task is canceled, the canceling token is recovered by observing that task, which costs a throw and catch that the BCL avoids. + + #### TaskCompletionSource * `void SetCanceled(CancellationToken)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcompletionsource-1.setcanceled?view=net-11.0#system-threading-tasks-taskcompletionsource-1-setcanceled(system-threading-cancellationtoken)) + * `void SetFromTask(Task)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcompletionsource-1.setfromtask?view=net-11.0) + * Note: When the supplied task is canceled, the canceling token is recovered by observing that task, which costs a throw and catch that the BCL avoids. + * `bool TrySetFromTask(Task)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcompletionsource-1.trysetfromtask?view=net-11.0) + * Note: When the supplied task is canceled, the canceling token is recovered by observing that task, which costs a throw and catch that the BCL avoids. #### TaskWhenEach @@ -1358,51 +1340,36 @@ * `TimeSpan FromSeconds(long, long, long)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.fromseconds?view=net-11.0#system-timespan-fromseconds(system-int64-system-int64-system-int64)) * `int HoursPerDay` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.hoursperday?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. * `int Microseconds` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.microseconds?view=net-11.0) * `long MicrosecondsPerDay` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.microsecondsperday?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. * `long MicrosecondsPerHour` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.microsecondsperhour?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. * `long MicrosecondsPerMillisecond` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.microsecondspermillisecond?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. * `long MicrosecondsPerMinute` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.microsecondsperminute?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. * `long MicrosecondsPerSecond` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.microsecondspersecond?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. * `long MillisecondsPerDay` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.millisecondsperday?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. * `long MillisecondsPerHour` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.millisecondsperhour?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. * `long MillisecondsPerMinute` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.millisecondsperminute?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. * `long MillisecondsPerSecond` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.millisecondspersecond?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. * `long MinutesPerDay` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.minutesperday?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. * `long MinutesPerHour` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.minutesperhour?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. * `int Nanoseconds` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.nanoseconds?view=net-11.0) * `long SecondsPerDay` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.secondsperday?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. * `long SecondsPerHour` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.secondsperhour?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. * `long SecondsPerMinute` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.secondsperminute?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. #### Type @@ -1629,10 +1596,8 @@ * `Span AsSpan(List?)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.collectionsmarshal.asspan?view=net-11.0) * Note: Reads the list's private backing array via reflection on this target. - * Note: Reads the list's private backing array via reflection on this target. * `void SetCount(List, int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.collectionsmarshal.setcount?view=net-11.0) * Note: When growing, new elements are default(T); the BCL exposes uninitialized data. - * Note: When growing, new elements are default(T); the BCL exposes uninitialized data. #### BitOperations @@ -1649,10 +1614,8 @@ * `bool FixedTimeEquals(ReadOnlySpan, byte)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicoperations.fixedtimeequals?view=net-11.0#system-security-cryptography-cryptographicoperations-fixedtimeequals(system-readonlyspan((system-byte))-system-byte)) * Note: Best-effort constant-time comparison; a managed loop cannot offer the same guarantees as the BCL intrinsic. - * Note: Best-effort constant-time comparison; a managed loop cannot offer the same guarantees as the BCL intrinsic. * `bool FixedTimeEquals(ReadOnlySpan, ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicoperations.fixedtimeequals?view=net-11.0#system-security-cryptography-cryptographicoperations-fixedtimeequals(system-readonlyspan((system-byte))-system-readonlyspan((system-byte)))) * Note: Best-effort constant-time comparison; a managed loop cannot offer the same guarantees as the BCL intrinsic. - * Note: Best-effort constant-time comparison; a managed loop cannot offer the same guarantees as the BCL intrinsic. * `byte[] HashData(HashAlgorithmName, byte[])` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicoperations.hashdata?view=net-11.0#system-security-cryptography-cryptographicoperations-hashdata(system-security-cryptography-hashalgorithmname-system-byte())) * `int HashData(HashAlgorithmName, ReadOnlySpan, Span)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicoperations.hashdata?view=net-11.0#system-security-cryptography-cryptographicoperations-hashdata(system-security-cryptography-hashalgorithmname-system-readonlyspan((system-byte))-system-span((system-byte)))) * `byte[] HashData(HashAlgorithmName, ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicoperations.hashdata?view=net-11.0#system-security-cryptography-cryptographicoperations-hashdata(system-security-cryptography-hashalgorithmname-system-readonlyspan((system-byte)))) @@ -1679,7 +1642,6 @@ * `ValueTask VerifyHmacAsync(HashAlgorithmName, ReadOnlyMemory, Stream, ReadOnlyMemory, CancellationToken)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicoperations.verifyhmacasync?view=net-11.0#system-security-cryptography-cryptographicoperations-verifyhmacasync(system-security-cryptography-hashalgorithmname-system-readonlymemory((system-byte))-system-io-stream-system-readonlymemory((system-byte))-system-threading-cancellationtoken)) * `void ZeroMemory(Span)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicoperations.zeromemory?view=net-11.0) * Note: Best-effort zeroing; unlike the BCL intrinsic the JIT may elide the clear if the buffer is not observed afterwards. - * Note: Best-effort zeroing; unlike the BCL intrinsic the JIT may elide the clear if the buffer is not observed afterwards. #### Utf16 @@ -1688,8 +1650,6 @@ * `bool IsValid(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.unicode.utf16.isvalid?view=net-11.0) -#### TaskCompletionSource - #### UnreachableException #### IUnion diff --git a/assemblySize.include.md b/assemblySize.include.md index 76f12345..ef20e168 100644 --- a/assemblySize.include.md +++ b/assemblySize.include.md @@ -2,24 +2,24 @@ | | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability | |----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------| -| netstandard2.0 | 8.0KB | 368.0KB | +360.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netstandard2.1 | 8.5KB | 323.5KB | +315.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net461 | 8.5KB | 367.0KB | +358.5KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | -| net462 | 7.0KB | 372.0KB | +365.0KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | -| net47 | 7.0KB | 371.5KB | +364.5KB | +7.5KB | +6.5KB | +9.0KB | +12.0KB | -| net471 | 8.5KB | 369.5KB | +361.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | -| net472 | 8.5KB | 368.0KB | +359.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net48 | 8.5KB | 368.0KB | +359.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net481 | 8.5KB | 368.0KB | +359.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.0 | 9.0KB | 347.0KB | +338.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.1 | 9.0KB | 327.0KB | +318.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.2 | 9.0KB | 327.0KB | +318.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp3.0 | 9.5KB | 323.0KB | +313.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp3.1 | 9.5KB | 321.0KB | +311.5KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | -| net5.0 | 9.5KB | 285.5KB | +276.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net6.0 | 10.0KB | 227.0KB | +217.0KB | +9.5KB | +6.5KB | +512bytes | +3.5KB | -| net7.0 | 10.0KB | 192.0KB | +182.0KB | +9.5KB | +6.0KB | +1.0KB | +3.5KB | -| net8.0 | 9.5KB | 162.5KB | +153.0KB | +8.5KB | | +512bytes | +3.0KB | +| netstandard2.0 | 8.0KB | 369.0KB | +361.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netstandard2.1 | 8.5KB | 324.5KB | +316.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net461 | 8.5KB | 368.0KB | +359.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net462 | 7.0KB | 373.0KB | +366.0KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | +| net47 | 7.0KB | 372.5KB | +365.5KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | +| net471 | 8.5KB | 370.5KB | +362.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | +| net472 | 8.5KB | 369.0KB | +360.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net48 | 8.5KB | 369.0KB | +360.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net481 | 8.5KB | 369.0KB | +360.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.0 | 9.0KB | 348.0KB | +339.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| netcoreapp2.1 | 9.0KB | 328.0KB | +319.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| netcoreapp2.2 | 9.0KB | 328.0KB | +319.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| netcoreapp3.0 | 9.5KB | 324.0KB | +314.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp3.1 | 9.5KB | 322.5KB | +313.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| net5.0 | 9.5KB | 286.5KB | +277.0KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | +| net6.0 | 10.0KB | 228.5KB | +218.5KB | +9.5KB | +6.5KB | +512bytes | +3.5KB | +| net7.0 | 10.0KB | 193.5KB | +183.5KB | +9.5KB | +6.0KB | +1.0KB | +3.5KB | +| net8.0 | 9.5KB | 164.0KB | +154.5KB | +8.0KB | | +512bytes | +3.0KB | | net9.0 | 9.5KB | 114.0KB | +104.5KB | +8.5KB | | +512bytes | +3.5KB | | net10.0 | 10.0KB | 92.0KB | +82.0KB | +8.5KB | | +512bytes | +3.5KB | | net11.0 | 10.0KB | 21.0KB | +11.0KB | +9.0KB | | +512bytes | +3.5KB | @@ -29,24 +29,24 @@ | | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability | |----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------| -| netstandard2.0 | 8.0KB | 539.7KB | +531.7KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netstandard2.1 | 8.5KB | 468.6KB | +460.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net461 | 8.5KB | 539.8KB | +531.3KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | -| net462 | 7.0KB | 544.8KB | +537.8KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | -| net47 | 7.0KB | 544.0KB | +537.0KB | +15.2KB | +8.2KB | +13.9KB | +17.4KB | -| net471 | 8.5KB | 541.7KB | +533.2KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | -| net472 | 8.5KB | 539.1KB | +530.6KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net48 | 8.5KB | 539.1KB | +530.6KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net481 | 8.5KB | 539.1KB | +530.6KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.0 | 9.0KB | 508.2KB | +499.2KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.1 | 9.0KB | 475.8KB | +466.8KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.2 | 9.0KB | 475.8KB | +466.8KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp3.0 | 9.5KB | 465.1KB | +455.6KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp3.1 | 9.5KB | 463.1KB | +453.6KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | -| net5.0 | 9.5KB | 409.5KB | +400.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net6.0 | 10.0KB | 331.0KB | +321.0KB | +17.2KB | +8.2KB | +1.1KB | +4.2KB | -| net7.0 | 10.0KB | 277.7KB | +267.7KB | +17.1KB | +7.4KB | +1.6KB | +4.2KB | -| net8.0 | 9.5KB | 234.4KB | +224.9KB | +16.0KB | +299bytes | +1.1KB | +3.7KB | +| netstandard2.0 | 8.0KB | 541.1KB | +533.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netstandard2.1 | 8.5KB | 470.0KB | +461.5KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net461 | 8.5KB | 541.1KB | +532.6KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net462 | 7.0KB | 546.1KB | +539.1KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | +| net47 | 7.0KB | 545.4KB | +538.4KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | +| net471 | 8.5KB | 543.0KB | +534.5KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | +| net472 | 8.5KB | 540.4KB | +531.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net48 | 8.5KB | 540.4KB | +531.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net481 | 8.5KB | 540.4KB | +531.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.0 | 9.0KB | 509.5KB | +500.5KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| netcoreapp2.1 | 9.0KB | 477.2KB | +468.2KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| netcoreapp2.2 | 9.0KB | 477.2KB | +468.2KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| netcoreapp3.0 | 9.5KB | 466.5KB | +457.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp3.1 | 9.5KB | 464.9KB | +455.4KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| net5.0 | 9.5KB | 411.1KB | +401.6KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | +| net6.0 | 10.0KB | 333.2KB | +323.2KB | +17.2KB | +8.2KB | +1.1KB | +4.2KB | +| net7.0 | 10.0KB | 279.9KB | +269.9KB | +17.1KB | +7.4KB | +1.6KB | +4.2KB | +| net8.0 | 9.5KB | 236.5KB | +227.0KB | +15.5KB | +299bytes | +1.1KB | +3.7KB | | net9.0 | 9.5KB | 164.6KB | +155.1KB | +16.0KB | | +1.1KB | +4.2KB | | net10.0 | 10.0KB | 133.9KB | +123.9KB | +16.0KB | | +1.1KB | +4.2KB | | net11.0 | 10.0KB | 30.9KB | +20.9KB | +16.5KB | | +1.1KB | +4.2KB | diff --git a/readme.md b/readme.md index dca883a6..6e1774c1 100644 --- a/readme.md +++ b/readme.md @@ -13,34 +13,34 @@ The package targets `netstandard2.0` and is designed to support the following ru * `uap10` -**API count: 1091** +**API count: 1095** ### Per Target Framework | Target | APIs | | -- | -- | -| `net461` | 1022 | -| `net462` | 1022 | -| `net47` | 1021 | -| `net471` | 1020 | -| `net472` | 1016 | -| `net48` | 1016 | -| `net481` | 1016 | -| `netstandard2.0` | 1018 | -| `netstandard2.1` | 871 | -| `netcoreapp2.0` | 941 | -| `netcoreapp2.1` | 882 | -| `netcoreapp2.2` | 882 | -| `netcoreapp3.0` | 843 | -| `netcoreapp3.1` | 842 | -| `net5.0` | 714 | -| `net6.0` | 615 | -| `net7.0` | 462 | -| `net8.0` | 341 | +| `net461` | 1026 | +| `net462` | 1026 | +| `net47` | 1025 | +| `net471` | 1024 | +| `net472` | 1020 | +| `net48` | 1020 | +| `net481` | 1020 | +| `netstandard2.0` | 1022 | +| `netstandard2.1` | 875 | +| `netcoreapp2.0` | 945 | +| `netcoreapp2.1` | 886 | +| `netcoreapp2.2` | 886 | +| `netcoreapp3.0` | 847 | +| `netcoreapp3.1` | 846 | +| `net5.0` | 718 | +| `net6.0` | 619 | +| `net7.0` | 466 | +| `net8.0` | 345 | | `net9.0` | 232 | | `net10.0` | 178 | | `net11.0` | 58 | -| `uap10.0` | 1008 | +| `uap10.0` | 1012 | @@ -96,24 +96,24 @@ This project uses features from the newest stable SDK and C# language. As such c | | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability | |----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------| -| netstandard2.0 | 8.0KB | 368.0KB | +360.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netstandard2.1 | 8.5KB | 323.5KB | +315.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net461 | 8.5KB | 367.0KB | +358.5KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | -| net462 | 7.0KB | 371.5KB | +364.5KB | +8.0KB | +7.0KB | +8.0KB | +12.5KB | -| net47 | 7.0KB | 371.5KB | +364.5KB | +7.5KB | +6.5KB | +9.0KB | +12.0KB | -| net471 | 8.5KB | 369.5KB | +361.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | -| net472 | 8.5KB | 368.0KB | +359.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net48 | 8.5KB | 368.0KB | +359.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net481 | 8.5KB | 368.0KB | +359.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.0 | 9.0KB | 347.0KB | +338.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.1 | 9.0KB | 327.0KB | +318.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.2 | 9.0KB | 327.0KB | +318.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp3.0 | 9.5KB | 323.0KB | +313.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp3.1 | 9.5KB | 321.0KB | +311.5KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | -| net5.0 | 9.5KB | 285.5KB | +276.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net6.0 | 10.0KB | 227.0KB | +217.0KB | +9.5KB | +6.5KB | +512bytes | +3.5KB | -| net7.0 | 10.0KB | 192.0KB | +182.0KB | +9.5KB | +6.0KB | +1.0KB | +3.5KB | -| net8.0 | 9.5KB | 162.5KB | +153.0KB | +8.5KB | | +512bytes | +3.0KB | +| netstandard2.0 | 8.0KB | 369.0KB | +361.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netstandard2.1 | 8.5KB | 324.5KB | +316.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net461 | 8.5KB | 368.0KB | +359.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net462 | 7.0KB | 373.0KB | +366.0KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | +| net47 | 7.0KB | 372.5KB | +365.5KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | +| net471 | 8.5KB | 370.5KB | +362.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | +| net472 | 8.5KB | 369.0KB | +360.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net48 | 8.5KB | 369.0KB | +360.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net481 | 8.5KB | 369.0KB | +360.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.0 | 9.0KB | 348.0KB | +339.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| netcoreapp2.1 | 9.0KB | 328.0KB | +319.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| netcoreapp2.2 | 9.0KB | 328.0KB | +319.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| netcoreapp3.0 | 9.5KB | 324.0KB | +314.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp3.1 | 9.5KB | 322.5KB | +313.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| net5.0 | 9.5KB | 286.5KB | +277.0KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | +| net6.0 | 10.0KB | 228.5KB | +218.5KB | +9.5KB | +6.5KB | +512bytes | +3.5KB | +| net7.0 | 10.0KB | 193.5KB | +183.5KB | +9.5KB | +6.0KB | +1.0KB | +3.5KB | +| net8.0 | 9.5KB | 164.0KB | +154.5KB | +8.0KB | | +512bytes | +3.0KB | | net9.0 | 9.5KB | 114.0KB | +104.5KB | +8.5KB | | +512bytes | +3.5KB | | net10.0 | 10.0KB | 92.0KB | +82.0KB | +8.5KB | | +512bytes | +3.5KB | | net11.0 | 10.0KB | 21.0KB | +11.0KB | +9.0KB | | +512bytes | +3.5KB | @@ -123,24 +123,24 @@ This project uses features from the newest stable SDK and C# language. As such c | | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability | |----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------| -| netstandard2.0 | 8.0KB | 539.7KB | +531.7KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netstandard2.1 | 8.5KB | 468.6KB | +460.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net461 | 8.5KB | 539.8KB | +531.3KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | -| net462 | 7.0KB | 544.3KB | +537.3KB | +15.7KB | +8.7KB | +12.9KB | +17.9KB | -| net47 | 7.0KB | 544.0KB | +537.0KB | +15.2KB | +8.2KB | +13.9KB | +17.4KB | -| net471 | 8.5KB | 541.7KB | +533.2KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | -| net472 | 8.5KB | 539.1KB | +530.6KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net48 | 8.5KB | 539.1KB | +530.6KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net481 | 8.5KB | 539.1KB | +530.6KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.0 | 9.0KB | 508.2KB | +499.2KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.1 | 9.0KB | 475.8KB | +466.8KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.2 | 9.0KB | 475.8KB | +466.8KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp3.0 | 9.5KB | 465.1KB | +455.6KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp3.1 | 9.5KB | 463.1KB | +453.6KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | -| net5.0 | 9.5KB | 409.5KB | +400.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net6.0 | 10.0KB | 331.0KB | +321.0KB | +17.2KB | +8.2KB | +1.1KB | +4.2KB | -| net7.0 | 10.0KB | 277.7KB | +267.7KB | +17.1KB | +7.4KB | +1.6KB | +4.2KB | -| net8.0 | 9.5KB | 234.4KB | +224.9KB | +16.0KB | +299bytes | +1.1KB | +3.7KB | +| netstandard2.0 | 8.0KB | 541.1KB | +533.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netstandard2.1 | 8.5KB | 470.0KB | +461.5KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net461 | 8.5KB | 541.1KB | +532.6KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net462 | 7.0KB | 546.1KB | +539.1KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | +| net47 | 7.0KB | 545.4KB | +538.4KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | +| net471 | 8.5KB | 543.0KB | +534.5KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | +| net472 | 8.5KB | 540.4KB | +531.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net48 | 8.5KB | 540.4KB | +531.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net481 | 8.5KB | 540.4KB | +531.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.0 | 9.0KB | 509.5KB | +500.5KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| netcoreapp2.1 | 9.0KB | 477.2KB | +468.2KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| netcoreapp2.2 | 9.0KB | 477.2KB | +468.2KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| netcoreapp3.0 | 9.5KB | 466.5KB | +457.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp3.1 | 9.5KB | 464.9KB | +455.4KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| net5.0 | 9.5KB | 411.1KB | +401.6KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | +| net6.0 | 10.0KB | 333.2KB | +323.2KB | +17.2KB | +8.2KB | +1.1KB | +4.2KB | +| net7.0 | 10.0KB | 279.9KB | +269.9KB | +17.1KB | +7.4KB | +1.6KB | +4.2KB | +| net8.0 | 9.5KB | 236.5KB | +227.0KB | +15.5KB | +299bytes | +1.1KB | +3.7KB | | net9.0 | 9.5KB | 164.6KB | +155.1KB | +16.0KB | | +1.1KB | +4.2KB | | net10.0 | 10.0KB | 133.9KB | +123.9KB | +16.0KB | | +1.1KB | +4.2KB | | net11.0 | 10.0KB | 30.9KB | +20.9KB | +16.5KB | | +1.1KB | +4.2KB | @@ -766,10 +766,8 @@ The class `Polyfill` includes the following extension methods: * `bool IsBetween(char, char, char)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.char.isbetween?view=net-11.0) * `char ToLowerOrdinal(char)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.char.tolowerordinal?view=net-11.0) * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. - * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. * `char ToUpperOrdinal(char)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.char.toupperordinal?view=net-11.0) * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. - * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. #### ConcurrentBag @@ -791,7 +789,6 @@ The class `Polyfill` includes the following extension methods: * `bool Remove(TKey, TValue) where TKey : class where TValue : class` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.conditionalweaktable-2.remove?view=net-11.0#system-runtime-compilerservices-conditionalweaktable-2-remove(-0-1@)) * Note: Lookup and removal are not performed under the table lock, so the operation is not atomic with regard to concurrent mutations. - * Note: Lookup and removal are not performed under the table lock, so the operation is not atomic with regard to concurrent mutations. #### Console @@ -892,21 +889,15 @@ The class `Polyfill` includes the following extension methods: * `void EnsureCapacity(int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2.ensurecapacity?view=net-11.0) * Note: No-op on older targets; the BCL grows the backing storage. - * Note: No-op on older targets; the BCL grows the backing storage. * `DictionaryAlternateLookup GetAlternateLookup() where TKey : notnull` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2.getalternatelookup?view=net-11.0) * Note: Lookups are O(n) on older targets; the BCL is O(1). * Note: Returns the free-standing `DictionaryAlternateLookup` rather than the BCL's nested `Dictionary.AlternateLookup`. Use `var` for cross-target code. - * Note: Lookups are O(n) on older targets; the BCL is O(1). - * Note: Returns the free-standing `DictionaryAlternateLookup` rather than the BCL's nested `Dictionary.AlternateLookup`. Use `var` for cross-target code. * `void TrimExcess(int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2.trimexcess?view=net-11.0#system-collections-generic-dictionary-2-trimexcess(system-int32)) * Note: No-op on older targets; the BCL shrinks the backing storage. - * Note: No-op on older targets; the BCL shrinks the backing storage. * `void TrimExcess()` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2.trimexcess?view=net-11.0#system-collections-generic-dictionary-2-trimexcess) * Note: No-op on older targets; the BCL shrinks the backing storage. - * Note: No-op on older targets; the BCL shrinks the backing storage. * `bool TryGetAlternateLookup(DictionaryAlternateLookup) where TKey : notnull` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2.trygetalternatelookup?view=net-11.0) * Note: Lookups are O(n) on older targets; the BCL is O(1). - * Note: Lookups are O(n) on older targets; the BCL is O(1). #### DictionaryEntry @@ -965,7 +956,6 @@ The class `Polyfill` includes the following extension methods: * `Encoding Latin1` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.encoding.latin1?view=net-11.0) * `ReadOnlySpan Preamble` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.encoding.preamble?view=net-11.0) * Note: Allocates a new array on each access, unlike the BCL property which returns a cached span. - * Note: Allocates a new array on each access, unlike the BCL property which returns a cached span. #### Enum @@ -1090,18 +1080,13 @@ The class `Polyfill` includes the following extension methods: * `void EnsureCapacity(int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.hashset-1.ensurecapacity?view=net-11.0#system-collections-generic-hashset-1-ensurecapacity(system-int32)) * Note: No-op on older targets; the BCL grows the backing storage. - * Note: No-op on older targets; the BCL grows the backing storage. * `HashSetAlternateLookup GetAlternateLookup()` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.hashset-1.getalternatelookup?view=net-11.0) * Note: Lookups are O(n) on older targets; the BCL is O(1). * Note: Returns the free-standing `HashSetAlternateLookup` rather than the BCL's nested `HashSet.AlternateLookup`. Use `var` for cross-target code. - * Note: Lookups are O(n) on older targets; the BCL is O(1). - * Note: Returns the free-standing `HashSetAlternateLookup` rather than the BCL's nested `HashSet.AlternateLookup`. Use `var` for cross-target code. * `void TrimExcess(int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.hashset-1.trimexcess?view=net-11.0#system-collections-generic-hashset-1-trimexcess(system-int32)) * Note: No-op on older targets; the BCL shrinks the backing storage. - * Note: No-op on older targets; the BCL shrinks the backing storage. * `bool TryGetAlternateLookup(HashSetAlternateLookup)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.hashset-1.trygetalternatelookup?view=net-11.0) * Note: Lookups are O(n) on older targets; the BCL is O(1). - * Note: Lookups are O(n) on older targets; the BCL is O(1). * `bool TryGetValue(T, T)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.hashset-1.trygetvalue?view=net-11.0) @@ -1319,12 +1304,9 @@ The class `Polyfill` includes the following extension methods: * `void EnsureCapacity(int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.ensurecapacity?view=net-11.0#system-collections-generic-list-1-ensurecapacity(system-int32)) * Note: No-op on older targets; the BCL grows the backing storage. * Note: Returns void on older targets; the BCL returns int (the new capacity). - * Note: No-op on older targets; the BCL grows the backing storage. - * Note: Returns void on older targets; the BCL returns int (the new capacity). * `void InsertRange(int, ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.collectionextensions.insertrange?view=net-11.0) * `void TrimExcess()` * Note: No-op on older targets; the BCL shrinks the backing storage. - * Note: No-op on older targets; the BCL shrinks the backing storage. #### Math @@ -1456,7 +1438,6 @@ The class `Polyfill` includes the following extension methods: * `Task<(byte[] StandardOutput, byte[] StandardError)> ReadAllBytesAsync(CancellationToken)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.readallbytesasync?view=net-11.0) * `IEnumerable ReadAllLines(TimeSpan?)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.readalllines?view=net-11.0) * Note: The timeout is measured from the point enumeration starts, since the returned sequence is lazy. - * Note: The timeout is measured from the point enumeration starts, since the returned sequence is lazy. * `IAsyncEnumerable ReadAllLinesAsync(CancellationToken)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.readalllinesasync?view=net-11.0) * `(string StandardOutput, string StandardError) ReadAllText(TimeSpan?)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.readalltext?view=net-11.0) * `Task<(string StandardOutput, string StandardError)> ReadAllTextAsync(CancellationToken)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.readalltextasync?view=net-11.0) @@ -1501,10 +1482,8 @@ The class `Polyfill` includes the following extension methods: * `void EnsureCapacity(int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.queue-1.ensurecapacity?view=net-11.0#system-collections-generic-queue-1-ensurecapacity(system-int32)) * Note: No-op on older targets; the BCL grows the backing storage. - * Note: No-op on older targets; the BCL grows the backing storage. * `void TrimExcess(int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.queue-1.trimexcess?view=net-11.0#system-collections-generic-queue-1-trimexcess(system-int32)) * Note: No-op on older targets; the BCL shrinks the backing storage. - * Note: No-op on older targets; the BCL shrinks the backing storage. * `bool TryDequeue(T)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.queue-1.trydequeue?view=net-11.0) * `bool TryPeek(T)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.queue-1.trypeek?view=net-11.0) @@ -1560,10 +1539,8 @@ The class `Polyfill` includes the following extension methods: * `bool StartsWith(string, StringComparison)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.memoryextensions.startswith?view=net-11.0#system-memoryextensions-startswith-1(system-readonlyspan((-0))-system-readonlyspan((-0)))) * `int ToLowerOrdinal(Span)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.memoryextensions.tolowerordinal?view=net-11.0) * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. - * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. * `int ToUpperOrdinal(Span)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.memoryextensions.toupperordinal?view=net-11.0) * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. - * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. * `bool TryNormalize(Span, int, NormalizationForm)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.stringnormalizationextensions.trynormalize?view=net-11.0#system-stringnormalizationextensions-trynormalize(system-readonlyspan((system-char))-system-span((system-char))-system-int32@-system-text-normalizationform)) @@ -1638,10 +1615,8 @@ The class `Polyfill` includes the following extension methods: * `bool Equals(Rune, StringComparison)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.rune.equals?view=net-11.0#system-text-rune-equals(system-text-rune-system-stringcomparison)) * `Rune ToLowerOrdinal(Rune)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.rune.tolowerordinal?view=net-11.0) * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. - * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. * `Rune ToUpperOrdinal(Rune)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.rune.toupperordinal?view=net-11.0) * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. - * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. #### RuntimeFeature @@ -1782,10 +1757,8 @@ The class `Polyfill` includes the following extension methods: * `void EnsureCapacity(int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.stack-1.ensurecapacity?view=net-11.0) * Note: No-op on older targets; the BCL grows the backing storage. - * Note: No-op on older targets; the BCL grows the backing storage. * `void TrimExcess(int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.stack-1.trimexcess?view=net-11.0#system-collections-generic-stack-1-trimexcess(system-int32)) * Note: No-op on older targets; the BCL shrinks the backing storage. - * Note: No-op on older targets; the BCL shrinks the backing storage. * `bool TryPeek(T)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.stack-1.trypeek?view=net-11.0) * `bool TryPop(T)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.stack-1.trypop?view=net-11.0) @@ -1837,10 +1810,8 @@ The class `Polyfill` includes the following extension methods: * `bool StartsWith(char)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.string.startswith?view=net-11.0#system-string-startswith(system-char)) * `string ToLowerOrdinal()` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.string.tolowerordinal?view=net-11.0) * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. - * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. * `string ToUpperOrdinal()` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.string.toupperordinal?view=net-11.0) * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. - * Note: Derived from invariant casing, so the mapping follows the Unicode version of the running framework rather than the one net11 is built against. * `bool TryCopyTo(Span)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.string.trycopyto?view=net-11.0) * `string Concat(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.string.concat?view=net-11.0#system-string-concat(system-readonlyspan((system-object)))) * `string Concat(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.string.concat?view=net-11.0#system-string-concat(system-readonlyspan((system-string)))) @@ -1878,7 +1849,6 @@ The class `Polyfill` includes the following extension methods: * `void CopyTo(int, Span, int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.copyto?view=net-11.0#system-text-stringbuilder-copyto(system-int32-system-span((system-char))-system-int32)) * `StringBuilderRuneEnumerator EnumerateRunes()` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.enumeraterunes?view=net-11.0) * Note: Enumerates over a snapshot of the content taken when enumeration starts, so changes made to the StringBuilder while enumerating are not observed. - * Note: Enumerates over a snapshot of the content taken when enumeration starts, so changes made to the StringBuilder while enumerating are not observed. * `bool Equals(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.equals?view=net-11.0#system-text-stringbuilder-equals(system-readonlyspan((system-char)))) * `ChunkEnumerator GetChunks()` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.getchunks?view=net-11.0) * `Rune GetRuneAt(int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.getruneat?view=net-11.0) @@ -1911,9 +1881,21 @@ The class `Polyfill` includes the following extension methods: * `Task WaitAsync(TimeSpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task-1.waitasync?view=net-11.0#system-threading-tasks-task-1-waitasync(system-timespan)) +#### TaskCompletionSource + + * `void SetFromTask(Task)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcompletionsource.setfromtask?view=net-11.0) + * Note: When the supplied task is canceled, the canceling token is recovered by observing that task, which costs a throw and catch that the BCL avoids. + * `bool TrySetFromTask(Task)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcompletionsource.trysetfromtask?view=net-11.0) + * Note: When the supplied task is canceled, the canceling token is recovered by observing that task, which costs a throw and catch that the BCL avoids. + + #### TaskCompletionSource * `void SetCanceled(CancellationToken)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcompletionsource-1.setcanceled?view=net-11.0#system-threading-tasks-taskcompletionsource-1-setcanceled(system-threading-cancellationtoken)) + * `void SetFromTask(Task)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcompletionsource-1.setfromtask?view=net-11.0) + * Note: When the supplied task is canceled, the canceling token is recovered by observing that task, which costs a throw and catch that the BCL avoids. + * `bool TrySetFromTask(Task)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcompletionsource-1.trysetfromtask?view=net-11.0) + * Note: When the supplied task is canceled, the canceling token is recovered by observing that task, which costs a throw and catch that the BCL avoids. #### TaskWhenEach @@ -1973,51 +1955,36 @@ The class `Polyfill` includes the following extension methods: * `TimeSpan FromSeconds(long, long, long)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.fromseconds?view=net-11.0#system-timespan-fromseconds(system-int64-system-int64-system-int64)) * `int HoursPerDay` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.hoursperday?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. * `int Microseconds` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.microseconds?view=net-11.0) * `long MicrosecondsPerDay` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.microsecondsperday?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. * `long MicrosecondsPerHour` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.microsecondsperhour?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. * `long MicrosecondsPerMillisecond` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.microsecondspermillisecond?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. * `long MicrosecondsPerMinute` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.microsecondsperminute?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. * `long MicrosecondsPerSecond` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.microsecondspersecond?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. * `long MillisecondsPerDay` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.millisecondsperday?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. * `long MillisecondsPerHour` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.millisecondsperhour?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. * `long MillisecondsPerMinute` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.millisecondsperminute?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. * `long MillisecondsPerSecond` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.millisecondspersecond?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. * `long MinutesPerDay` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.minutesperday?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. * `long MinutesPerHour` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.minutesperhour?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. * `int Nanoseconds` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.nanoseconds?view=net-11.0) * `long SecondsPerDay` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.secondsperday?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. * `long SecondsPerHour` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.secondsperhour?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. * `long SecondsPerMinute` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.timespan.secondsperminute?view=net-11.0) * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. - * Note: A property rather than a const, since extension members cannot declare constants, so it cannot be used in a constant expression. #### Type @@ -2244,10 +2211,8 @@ The class `Polyfill` includes the following extension methods: * `Span AsSpan(List?)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.collectionsmarshal.asspan?view=net-11.0) * Note: Reads the list's private backing array via reflection on this target. - * Note: Reads the list's private backing array via reflection on this target. * `void SetCount(List, int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.collectionsmarshal.setcount?view=net-11.0) * Note: When growing, new elements are default(T); the BCL exposes uninitialized data. - * Note: When growing, new elements are default(T); the BCL exposes uninitialized data. #### BitOperations @@ -2264,10 +2229,8 @@ The class `Polyfill` includes the following extension methods: * `bool FixedTimeEquals(ReadOnlySpan, byte)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicoperations.fixedtimeequals?view=net-11.0#system-security-cryptography-cryptographicoperations-fixedtimeequals(system-readonlyspan((system-byte))-system-byte)) * Note: Best-effort constant-time comparison; a managed loop cannot offer the same guarantees as the BCL intrinsic. - * Note: Best-effort constant-time comparison; a managed loop cannot offer the same guarantees as the BCL intrinsic. * `bool FixedTimeEquals(ReadOnlySpan, ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicoperations.fixedtimeequals?view=net-11.0#system-security-cryptography-cryptographicoperations-fixedtimeequals(system-readonlyspan((system-byte))-system-readonlyspan((system-byte)))) * Note: Best-effort constant-time comparison; a managed loop cannot offer the same guarantees as the BCL intrinsic. - * Note: Best-effort constant-time comparison; a managed loop cannot offer the same guarantees as the BCL intrinsic. * `byte[] HashData(HashAlgorithmName, byte[])` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicoperations.hashdata?view=net-11.0#system-security-cryptography-cryptographicoperations-hashdata(system-security-cryptography-hashalgorithmname-system-byte())) * `int HashData(HashAlgorithmName, ReadOnlySpan, Span)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicoperations.hashdata?view=net-11.0#system-security-cryptography-cryptographicoperations-hashdata(system-security-cryptography-hashalgorithmname-system-readonlyspan((system-byte))-system-span((system-byte)))) * `byte[] HashData(HashAlgorithmName, ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicoperations.hashdata?view=net-11.0#system-security-cryptography-cryptographicoperations-hashdata(system-security-cryptography-hashalgorithmname-system-readonlyspan((system-byte)))) @@ -2294,7 +2257,6 @@ The class `Polyfill` includes the following extension methods: * `ValueTask VerifyHmacAsync(HashAlgorithmName, ReadOnlyMemory, Stream, ReadOnlyMemory, CancellationToken)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicoperations.verifyhmacasync?view=net-11.0#system-security-cryptography-cryptographicoperations-verifyhmacasync(system-security-cryptography-hashalgorithmname-system-readonlymemory((system-byte))-system-io-stream-system-readonlymemory((system-byte))-system-threading-cancellationtoken)) * `void ZeroMemory(Span)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicoperations.zeromemory?view=net-11.0) * Note: Best-effort zeroing; unlike the BCL intrinsic the JIT may elide the clear if the buffer is not observed afterwards. - * Note: Best-effort zeroing; unlike the BCL intrinsic the JIT may elide the clear if the buffer is not observed afterwards. #### Utf16 @@ -2303,8 +2265,6 @@ The class `Polyfill` includes the following extension methods: * `bool IsValid(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.unicode.utf16.isvalid?view=net-11.0) -#### TaskCompletionSource - #### UnreachableException #### IUnion diff --git a/src/ApiBuilderTests/BuildApiTest.cs b/src/ApiBuilderTests/BuildApiTest.cs index d59738a8..8f3b67ba 100644 --- a/src/ApiBuilderTests/BuildApiTest.cs +++ b/src/ApiBuilderTests/BuildApiTest.cs @@ -2,6 +2,11 @@ public class BuildApiTest { static string polyfillDir = Path.Combine(ProjectFiles.SolutionDirectory, "Polyfill"); + // Section headers already emitted by WriteExtensions. A recreated type whose later + // members arrive through an extension(Type) (TaskCompletionSource, for example) would + // otherwise get a second header of the same name further down the document. + static HashSet writtenSections = []; + [Test] // The per-target-framework counts are read out of Split, so it has to be written first [DependsOn(nameof(SplitterTests.Run))] @@ -12,6 +17,7 @@ public Task RunWithRoslyn() { var md = Path.Combine(ProjectFiles.SolutionDirectory, "..", "api_list.include.md"); File.Delete(md); + writtenSections.Clear(); using var writer = File.CreateText(md); writer.NewLine = "\n"; var count = 0; @@ -170,6 +176,7 @@ static int WriteExtensions(StreamWriter writer, int count) var instanceMethodsForType = instanceMethods .Where(_ => SectionName(_) == name) .ToList(); + writtenSections.Add(name); writer.WriteLine($"#### {name}"); writer.WriteLine(); WriteSectionGate(name, writer); @@ -325,10 +332,17 @@ static List ReadPropertiesForFiles(IEnumerable files) static void WriteType(string name, StreamWriter writer, ref int count) { + count++; + + // The type's extension members already opened a section under this name + if (writtenSections.Contains(name)) + { + return; + } + writer.WriteLine($"#### {name}"); writer.WriteLine(); WriteSectionGate(name, writer); - count++; } static void WriteHelper(string name, StreamWriter writer, ref int count) => @@ -336,6 +350,11 @@ static void WriteHelper(string name, StreamWriter writer, ref int count) => static void WriteTypeMethods(string name, StreamWriter writer, ref int count, IEnumerable methods) { + if (writtenSections.Contains(name)) + { + throw new($"#### {name} was already written by WriteExtensions, so these members would land under a duplicate header. Merge the files, or list the type with WriteType."); + } + writer.WriteLine($"#### {name}"); writer.WriteLine(); WriteSectionGate(name, writer); diff --git a/src/ApiBuilderTests/LinkReader.cs b/src/ApiBuilderTests/LinkReader.cs index d98a6b85..6ef49223 100644 --- a/src/ApiBuilderTests/LinkReader.cs +++ b/src/ApiBuilderTests/LinkReader.cs @@ -3,10 +3,13 @@ // A //Link: or //Note: can sit above the declaration, or between its attributes and // its modifiers. GetLeadingTrivia only covers the first of those, since an attribute // list is part of the declaration, so the modifiers are scanned as well. + // Where there is no attribute list the first token IS the first modifier, so the two + // sequences overlap and are deduplicated by position. static IEnumerable LeadingComments(this Member member) => member .GetLeadingTrivia() - .Concat(member.Modifiers.SelectMany(_ => _.LeadingTrivia)); + .Concat(member.Modifiers.SelectMany(_ => _.LeadingTrivia)) + .DistinctBy(_ => _.SpanStart); public static bool TryGetReference(this Member member, [NotNullWhen(true)] out string? reference) { diff --git a/src/Consume/Consume.cs b/src/Consume/Consume.cs index e0447ab9..c32917e2 100644 --- a/src/Consume/Consume.cs +++ b/src/Consume/Consume.cs @@ -1914,6 +1914,8 @@ async Task Parallel_Methods() void TaskCompletionSource_NonGeneric_Methods() { var tcs = new TaskCompletionSource(); + tcs.SetFromTask(Task.CompletedTask); + var trySet = new TaskCompletionSource().TrySetFromTask(Task.CompletedTask); } void TaskCompletionSource_Generic_Methods() @@ -1921,6 +1923,8 @@ void TaskCompletionSource_Generic_Methods() var completionSource = new TaskCompletionSource(); var tokenSource = new CancellationTokenSource(); completionSource.SetCanceled(tokenSource.Token); + completionSource.SetFromTask(Task.FromResult(1)); + var trySet = new TaskCompletionSource().TrySetFromTask(Task.FromResult(1)); } void TimeSpan_Methods() diff --git a/src/Polyfill/Polyfill_TaskCompletionSource.cs b/src/Polyfill/Polyfill_TaskCompletionSource.cs index 2458b4e8..9c185221 100644 --- a/src/Polyfill/Polyfill_TaskCompletionSource.cs +++ b/src/Polyfill/Polyfill_TaskCompletionSource.cs @@ -1,4 +1,4 @@ -#if !NET +#if !NET9_0_OR_GREATER namespace Polyfills; @@ -8,6 +8,7 @@ namespace Polyfills; static partial class Polyfill { +#if !NET /// /// Transitions the underlying into the state /// using the specified token. @@ -24,5 +25,106 @@ public static void SetCanceled( throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); } +#endif + + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcompletionsource.setfromtask?view=net-11.0 + //Note: When the supplied task is canceled, the canceling token is recovered by observing that task, which costs a throw and catch that the BCL avoids. + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcompletionsource.trysetfromtask?view=net-11.0 + //Note: When the supplied task is canceled, the canceling token is recovered by observing that task, which costs a throw and catch that the BCL avoids. + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcompletionsource-1.setfromtask?view=net-11.0 + //Note: When the supplied task is canceled, the canceling token is recovered by observing that task, which costs a throw and catch that the BCL avoids. + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcompletionsource-1.trysetfromtask?view=net-11.0 + //Note: When the supplied task is canceled, the canceling token is recovered by observing that task, which costs a throw and catch that the BCL avoids. + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(completedTask.Result), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + + static void GuardCompletedTask(Task completedTask) + { + if (completedTask == null) + { + throw new ArgumentNullException(nameof(completedTask)); + } + + if (!completedTask.IsCompleted) + { + throw new ArgumentException("The provided task must have already completed.", nameof(completedTask)); + } + } + + // Task does not expose the token that canceled it, but the OperationCanceledException + // thrown when the completed task is observed does. + static CancellationToken CancellationTokenOf(Task canceledTask) + { + try + { + canceledTask.GetAwaiter().GetResult(); + } + catch (OperationCanceledException exception) + { + return exception.CancellationToken; + } + catch + { + } + + return default; + } } #endif diff --git a/src/Split/net461/Polyfill_TaskCompletionSource.cs b/src/Split/net461/Polyfill_TaskCompletionSource.cs index a4955eb7..3729451c 100644 --- a/src/Split/net461/Polyfill_TaskCompletionSource.cs +++ b/src/Split/net461/Polyfill_TaskCompletionSource.cs @@ -20,4 +20,84 @@ public static void SetCanceled( } throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(completedTask.Result), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + static void GuardCompletedTask(Task completedTask) + { + if (completedTask == null) + { + throw new ArgumentNullException(nameof(completedTask)); + } + if (!completedTask.IsCompleted) + { + throw new ArgumentException("The provided task must have already completed.", nameof(completedTask)); + } + } + static CancellationToken CancellationTokenOf(Task canceledTask) + { + try + { + canceledTask.GetAwaiter().GetResult(); + } + catch (OperationCanceledException exception) + { + return exception.CancellationToken; + } + catch + { + } + return default; + } } diff --git a/src/Split/net462/Polyfill_TaskCompletionSource.cs b/src/Split/net462/Polyfill_TaskCompletionSource.cs index a4955eb7..3729451c 100644 --- a/src/Split/net462/Polyfill_TaskCompletionSource.cs +++ b/src/Split/net462/Polyfill_TaskCompletionSource.cs @@ -20,4 +20,84 @@ public static void SetCanceled( } throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(completedTask.Result), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + static void GuardCompletedTask(Task completedTask) + { + if (completedTask == null) + { + throw new ArgumentNullException(nameof(completedTask)); + } + if (!completedTask.IsCompleted) + { + throw new ArgumentException("The provided task must have already completed.", nameof(completedTask)); + } + } + static CancellationToken CancellationTokenOf(Task canceledTask) + { + try + { + canceledTask.GetAwaiter().GetResult(); + } + catch (OperationCanceledException exception) + { + return exception.CancellationToken; + } + catch + { + } + return default; + } } diff --git a/src/Split/net47/Polyfill_TaskCompletionSource.cs b/src/Split/net47/Polyfill_TaskCompletionSource.cs index a4955eb7..3729451c 100644 --- a/src/Split/net47/Polyfill_TaskCompletionSource.cs +++ b/src/Split/net47/Polyfill_TaskCompletionSource.cs @@ -20,4 +20,84 @@ public static void SetCanceled( } throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(completedTask.Result), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + static void GuardCompletedTask(Task completedTask) + { + if (completedTask == null) + { + throw new ArgumentNullException(nameof(completedTask)); + } + if (!completedTask.IsCompleted) + { + throw new ArgumentException("The provided task must have already completed.", nameof(completedTask)); + } + } + static CancellationToken CancellationTokenOf(Task canceledTask) + { + try + { + canceledTask.GetAwaiter().GetResult(); + } + catch (OperationCanceledException exception) + { + return exception.CancellationToken; + } + catch + { + } + return default; + } } diff --git a/src/Split/net471/Polyfill_TaskCompletionSource.cs b/src/Split/net471/Polyfill_TaskCompletionSource.cs index a4955eb7..3729451c 100644 --- a/src/Split/net471/Polyfill_TaskCompletionSource.cs +++ b/src/Split/net471/Polyfill_TaskCompletionSource.cs @@ -20,4 +20,84 @@ public static void SetCanceled( } throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(completedTask.Result), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + static void GuardCompletedTask(Task completedTask) + { + if (completedTask == null) + { + throw new ArgumentNullException(nameof(completedTask)); + } + if (!completedTask.IsCompleted) + { + throw new ArgumentException("The provided task must have already completed.", nameof(completedTask)); + } + } + static CancellationToken CancellationTokenOf(Task canceledTask) + { + try + { + canceledTask.GetAwaiter().GetResult(); + } + catch (OperationCanceledException exception) + { + return exception.CancellationToken; + } + catch + { + } + return default; + } } diff --git a/src/Split/net472/Polyfill_TaskCompletionSource.cs b/src/Split/net472/Polyfill_TaskCompletionSource.cs index a4955eb7..3729451c 100644 --- a/src/Split/net472/Polyfill_TaskCompletionSource.cs +++ b/src/Split/net472/Polyfill_TaskCompletionSource.cs @@ -20,4 +20,84 @@ public static void SetCanceled( } throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(completedTask.Result), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + static void GuardCompletedTask(Task completedTask) + { + if (completedTask == null) + { + throw new ArgumentNullException(nameof(completedTask)); + } + if (!completedTask.IsCompleted) + { + throw new ArgumentException("The provided task must have already completed.", nameof(completedTask)); + } + } + static CancellationToken CancellationTokenOf(Task canceledTask) + { + try + { + canceledTask.GetAwaiter().GetResult(); + } + catch (OperationCanceledException exception) + { + return exception.CancellationToken; + } + catch + { + } + return default; + } } diff --git a/src/Split/net48/Polyfill_TaskCompletionSource.cs b/src/Split/net48/Polyfill_TaskCompletionSource.cs index a4955eb7..3729451c 100644 --- a/src/Split/net48/Polyfill_TaskCompletionSource.cs +++ b/src/Split/net48/Polyfill_TaskCompletionSource.cs @@ -20,4 +20,84 @@ public static void SetCanceled( } throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(completedTask.Result), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + static void GuardCompletedTask(Task completedTask) + { + if (completedTask == null) + { + throw new ArgumentNullException(nameof(completedTask)); + } + if (!completedTask.IsCompleted) + { + throw new ArgumentException("The provided task must have already completed.", nameof(completedTask)); + } + } + static CancellationToken CancellationTokenOf(Task canceledTask) + { + try + { + canceledTask.GetAwaiter().GetResult(); + } + catch (OperationCanceledException exception) + { + return exception.CancellationToken; + } + catch + { + } + return default; + } } diff --git a/src/Split/net481/Polyfill_TaskCompletionSource.cs b/src/Split/net481/Polyfill_TaskCompletionSource.cs index a4955eb7..3729451c 100644 --- a/src/Split/net481/Polyfill_TaskCompletionSource.cs +++ b/src/Split/net481/Polyfill_TaskCompletionSource.cs @@ -20,4 +20,84 @@ public static void SetCanceled( } throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(completedTask.Result), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + static void GuardCompletedTask(Task completedTask) + { + if (completedTask == null) + { + throw new ArgumentNullException(nameof(completedTask)); + } + if (!completedTask.IsCompleted) + { + throw new ArgumentException("The provided task must have already completed.", nameof(completedTask)); + } + } + static CancellationToken CancellationTokenOf(Task canceledTask) + { + try + { + canceledTask.GetAwaiter().GetResult(); + } + catch (OperationCanceledException exception) + { + return exception.CancellationToken; + } + catch + { + } + return default; + } } diff --git a/src/Split/net5.0/Polyfill_TaskCompletionSource.cs b/src/Split/net5.0/Polyfill_TaskCompletionSource.cs new file mode 100644 index 00000000..12144303 --- /dev/null +++ b/src/Split/net5.0/Polyfill_TaskCompletionSource.cs @@ -0,0 +1,89 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +using System.Threading; +using System.Threading.Tasks; +static partial class Polyfill +{ + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(completedTask.Result), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + static void GuardCompletedTask(Task completedTask) + { + if (completedTask == null) + { + throw new ArgumentNullException(nameof(completedTask)); + } + if (!completedTask.IsCompleted) + { + throw new ArgumentException("The provided task must have already completed.", nameof(completedTask)); + } + } + static CancellationToken CancellationTokenOf(Task canceledTask) + { + try + { + canceledTask.GetAwaiter().GetResult(); + } + catch (OperationCanceledException exception) + { + return exception.CancellationToken; + } + catch + { + } + return default; + } +} diff --git a/src/Split/net6.0/Polyfill_TaskCompletionSource.cs b/src/Split/net6.0/Polyfill_TaskCompletionSource.cs new file mode 100644 index 00000000..12144303 --- /dev/null +++ b/src/Split/net6.0/Polyfill_TaskCompletionSource.cs @@ -0,0 +1,89 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +using System.Threading; +using System.Threading.Tasks; +static partial class Polyfill +{ + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(completedTask.Result), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + static void GuardCompletedTask(Task completedTask) + { + if (completedTask == null) + { + throw new ArgumentNullException(nameof(completedTask)); + } + if (!completedTask.IsCompleted) + { + throw new ArgumentException("The provided task must have already completed.", nameof(completedTask)); + } + } + static CancellationToken CancellationTokenOf(Task canceledTask) + { + try + { + canceledTask.GetAwaiter().GetResult(); + } + catch (OperationCanceledException exception) + { + return exception.CancellationToken; + } + catch + { + } + return default; + } +} diff --git a/src/Split/net7.0/Polyfill_TaskCompletionSource.cs b/src/Split/net7.0/Polyfill_TaskCompletionSource.cs new file mode 100644 index 00000000..12144303 --- /dev/null +++ b/src/Split/net7.0/Polyfill_TaskCompletionSource.cs @@ -0,0 +1,89 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +using System.Threading; +using System.Threading.Tasks; +static partial class Polyfill +{ + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(completedTask.Result), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + static void GuardCompletedTask(Task completedTask) + { + if (completedTask == null) + { + throw new ArgumentNullException(nameof(completedTask)); + } + if (!completedTask.IsCompleted) + { + throw new ArgumentException("The provided task must have already completed.", nameof(completedTask)); + } + } + static CancellationToken CancellationTokenOf(Task canceledTask) + { + try + { + canceledTask.GetAwaiter().GetResult(); + } + catch (OperationCanceledException exception) + { + return exception.CancellationToken; + } + catch + { + } + return default; + } +} diff --git a/src/Split/net8.0/Polyfill_TaskCompletionSource.cs b/src/Split/net8.0/Polyfill_TaskCompletionSource.cs new file mode 100644 index 00000000..12144303 --- /dev/null +++ b/src/Split/net8.0/Polyfill_TaskCompletionSource.cs @@ -0,0 +1,89 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +using System.Threading; +using System.Threading.Tasks; +static partial class Polyfill +{ + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(completedTask.Result), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + static void GuardCompletedTask(Task completedTask) + { + if (completedTask == null) + { + throw new ArgumentNullException(nameof(completedTask)); + } + if (!completedTask.IsCompleted) + { + throw new ArgumentException("The provided task must have already completed.", nameof(completedTask)); + } + } + static CancellationToken CancellationTokenOf(Task canceledTask) + { + try + { + canceledTask.GetAwaiter().GetResult(); + } + catch (OperationCanceledException exception) + { + return exception.CancellationToken; + } + catch + { + } + return default; + } +} diff --git a/src/Split/netcoreapp2.0/Polyfill_TaskCompletionSource.cs b/src/Split/netcoreapp2.0/Polyfill_TaskCompletionSource.cs index a4955eb7..3729451c 100644 --- a/src/Split/netcoreapp2.0/Polyfill_TaskCompletionSource.cs +++ b/src/Split/netcoreapp2.0/Polyfill_TaskCompletionSource.cs @@ -20,4 +20,84 @@ public static void SetCanceled( } throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(completedTask.Result), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + static void GuardCompletedTask(Task completedTask) + { + if (completedTask == null) + { + throw new ArgumentNullException(nameof(completedTask)); + } + if (!completedTask.IsCompleted) + { + throw new ArgumentException("The provided task must have already completed.", nameof(completedTask)); + } + } + static CancellationToken CancellationTokenOf(Task canceledTask) + { + try + { + canceledTask.GetAwaiter().GetResult(); + } + catch (OperationCanceledException exception) + { + return exception.CancellationToken; + } + catch + { + } + return default; + } } diff --git a/src/Split/netcoreapp2.1/Polyfill_TaskCompletionSource.cs b/src/Split/netcoreapp2.1/Polyfill_TaskCompletionSource.cs index a4955eb7..3729451c 100644 --- a/src/Split/netcoreapp2.1/Polyfill_TaskCompletionSource.cs +++ b/src/Split/netcoreapp2.1/Polyfill_TaskCompletionSource.cs @@ -20,4 +20,84 @@ public static void SetCanceled( } throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(completedTask.Result), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + static void GuardCompletedTask(Task completedTask) + { + if (completedTask == null) + { + throw new ArgumentNullException(nameof(completedTask)); + } + if (!completedTask.IsCompleted) + { + throw new ArgumentException("The provided task must have already completed.", nameof(completedTask)); + } + } + static CancellationToken CancellationTokenOf(Task canceledTask) + { + try + { + canceledTask.GetAwaiter().GetResult(); + } + catch (OperationCanceledException exception) + { + return exception.CancellationToken; + } + catch + { + } + return default; + } } diff --git a/src/Split/netcoreapp2.2/Polyfill_TaskCompletionSource.cs b/src/Split/netcoreapp2.2/Polyfill_TaskCompletionSource.cs index a4955eb7..3729451c 100644 --- a/src/Split/netcoreapp2.2/Polyfill_TaskCompletionSource.cs +++ b/src/Split/netcoreapp2.2/Polyfill_TaskCompletionSource.cs @@ -20,4 +20,84 @@ public static void SetCanceled( } throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(completedTask.Result), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + static void GuardCompletedTask(Task completedTask) + { + if (completedTask == null) + { + throw new ArgumentNullException(nameof(completedTask)); + } + if (!completedTask.IsCompleted) + { + throw new ArgumentException("The provided task must have already completed.", nameof(completedTask)); + } + } + static CancellationToken CancellationTokenOf(Task canceledTask) + { + try + { + canceledTask.GetAwaiter().GetResult(); + } + catch (OperationCanceledException exception) + { + return exception.CancellationToken; + } + catch + { + } + return default; + } } diff --git a/src/Split/netcoreapp3.0/Polyfill_TaskCompletionSource.cs b/src/Split/netcoreapp3.0/Polyfill_TaskCompletionSource.cs index a4955eb7..3729451c 100644 --- a/src/Split/netcoreapp3.0/Polyfill_TaskCompletionSource.cs +++ b/src/Split/netcoreapp3.0/Polyfill_TaskCompletionSource.cs @@ -20,4 +20,84 @@ public static void SetCanceled( } throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(completedTask.Result), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + static void GuardCompletedTask(Task completedTask) + { + if (completedTask == null) + { + throw new ArgumentNullException(nameof(completedTask)); + } + if (!completedTask.IsCompleted) + { + throw new ArgumentException("The provided task must have already completed.", nameof(completedTask)); + } + } + static CancellationToken CancellationTokenOf(Task canceledTask) + { + try + { + canceledTask.GetAwaiter().GetResult(); + } + catch (OperationCanceledException exception) + { + return exception.CancellationToken; + } + catch + { + } + return default; + } } diff --git a/src/Split/netcoreapp3.1/Polyfill_TaskCompletionSource.cs b/src/Split/netcoreapp3.1/Polyfill_TaskCompletionSource.cs index a4955eb7..3729451c 100644 --- a/src/Split/netcoreapp3.1/Polyfill_TaskCompletionSource.cs +++ b/src/Split/netcoreapp3.1/Polyfill_TaskCompletionSource.cs @@ -20,4 +20,84 @@ public static void SetCanceled( } throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(completedTask.Result), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + static void GuardCompletedTask(Task completedTask) + { + if (completedTask == null) + { + throw new ArgumentNullException(nameof(completedTask)); + } + if (!completedTask.IsCompleted) + { + throw new ArgumentException("The provided task must have already completed.", nameof(completedTask)); + } + } + static CancellationToken CancellationTokenOf(Task canceledTask) + { + try + { + canceledTask.GetAwaiter().GetResult(); + } + catch (OperationCanceledException exception) + { + return exception.CancellationToken; + } + catch + { + } + return default; + } } diff --git a/src/Split/netstandard2.0/Polyfill_TaskCompletionSource.cs b/src/Split/netstandard2.0/Polyfill_TaskCompletionSource.cs index a4955eb7..3729451c 100644 --- a/src/Split/netstandard2.0/Polyfill_TaskCompletionSource.cs +++ b/src/Split/netstandard2.0/Polyfill_TaskCompletionSource.cs @@ -20,4 +20,84 @@ public static void SetCanceled( } throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(completedTask.Result), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + static void GuardCompletedTask(Task completedTask) + { + if (completedTask == null) + { + throw new ArgumentNullException(nameof(completedTask)); + } + if (!completedTask.IsCompleted) + { + throw new ArgumentException("The provided task must have already completed.", nameof(completedTask)); + } + } + static CancellationToken CancellationTokenOf(Task canceledTask) + { + try + { + canceledTask.GetAwaiter().GetResult(); + } + catch (OperationCanceledException exception) + { + return exception.CancellationToken; + } + catch + { + } + return default; + } } diff --git a/src/Split/netstandard2.1/Polyfill_TaskCompletionSource.cs b/src/Split/netstandard2.1/Polyfill_TaskCompletionSource.cs index a4955eb7..3729451c 100644 --- a/src/Split/netstandard2.1/Polyfill_TaskCompletionSource.cs +++ b/src/Split/netstandard2.1/Polyfill_TaskCompletionSource.cs @@ -20,4 +20,84 @@ public static void SetCanceled( } throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(completedTask.Result), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + static void GuardCompletedTask(Task completedTask) + { + if (completedTask == null) + { + throw new ArgumentNullException(nameof(completedTask)); + } + if (!completedTask.IsCompleted) + { + throw new ArgumentException("The provided task must have already completed.", nameof(completedTask)); + } + } + static CancellationToken CancellationTokenOf(Task canceledTask) + { + try + { + canceledTask.GetAwaiter().GetResult(); + } + catch (OperationCanceledException exception) + { + return exception.CancellationToken; + } + catch + { + } + return default; + } } diff --git a/src/Split/uap10.0/Polyfill_TaskCompletionSource.cs b/src/Split/uap10.0/Polyfill_TaskCompletionSource.cs index a4955eb7..3729451c 100644 --- a/src/Split/uap10.0/Polyfill_TaskCompletionSource.cs +++ b/src/Split/uap10.0/Polyfill_TaskCompletionSource.cs @@ -20,4 +20,84 @@ public static void SetCanceled( } throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + /// + /// Transitions the underlying into the same completion state as the supplied task. + /// + public static void SetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + if (!target.TrySetFromTask(completedTask)) + { + throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); + } + } + /// + /// Attempts to transition the underlying into the same completion state as the supplied task. + /// + public static bool TrySetFromTask( + this TaskCompletionSource target, + Task completedTask) + { + GuardCompletedTask(completedTask); + return completedTask.Status switch + { + TaskStatus.RanToCompletion => target.TrySetResult(completedTask.Result), + TaskStatus.Faulted => target.TrySetException(completedTask.Exception!.InnerExceptions), + _ => target.TrySetCanceled(CancellationTokenOf(completedTask)) + }; + } + static void GuardCompletedTask(Task completedTask) + { + if (completedTask == null) + { + throw new ArgumentNullException(nameof(completedTask)); + } + if (!completedTask.IsCompleted) + { + throw new ArgumentException("The provided task must have already completed.", nameof(completedTask)); + } + } + static CancellationToken CancellationTokenOf(Task canceledTask) + { + try + { + canceledTask.GetAwaiter().GetResult(); + } + catch (OperationCanceledException exception) + { + return exception.CancellationToken; + } + catch + { + } + return default; + } } diff --git a/src/Tests/PolyfillTests_TaskCompletionSource.cs b/src/Tests/PolyfillTests_TaskCompletionSource.cs index 4c97611f..1edfff49 100644 --- a/src/Tests/PolyfillTests_TaskCompletionSource.cs +++ b/src/Tests/PolyfillTests_TaskCompletionSource.cs @@ -68,4 +68,123 @@ public async Task TaskCompletionSource_SetCanceled_WithCancellationToken() Console.WriteLine("Task was canceled."); } } -} \ No newline at end of file + + [Test] + public async Task TaskCompletionSource_SetFromTask_RanToCompletion() + { + var completionSource = new TaskCompletionSource(); + completionSource.SetFromTask(Task.FromResult(42)); + + await completionSource.Task; + await Assert.That(completionSource.Task.Status).IsEqualTo(TaskStatus.RanToCompletion); + + var generic = new TaskCompletionSource(); + generic.SetFromTask(Task.FromResult("value")); + + await Assert.That(await generic.Task).IsEqualTo("value"); + } + + [Test] + public async Task TaskCompletionSource_SetFromTask_Faulted() + { + var source = new TaskCompletionSource(); + var first = new InvalidOperationException("first"); + var second = new FormatException("second"); + source.SetException(new Exception[] { first, second }); + + var target = new TaskCompletionSource(); + target.SetFromTask(source.Task); + + var exception = target.Task.Exception!; + await Assert.That(exception.InnerExceptions.Count).IsEqualTo(2); + // the original instances are carried over, not wrapped or flattened + await Assert.That(exception.InnerExceptions[0]).IsSameReferenceAs(first); + await Assert.That(exception.InnerExceptions[1]).IsSameReferenceAs(second); + } + + [Test] + public async Task TaskCompletionSource_SetFromTask_Canceled_PropagatesToken() + { + using var cancelSource = new CancellationTokenSource(); + cancelSource.Cancel(); + var token = cancelSource.Token; + + var source = new TaskCompletionSource(); + source.TrySetCanceled(token); + + var target = new TaskCompletionSource(); + target.SetFromTask(source.Task); + + await Assert.That(target.Task.Status).IsEqualTo(TaskStatus.Canceled); + + CancellationToken observed = default; + try + { + await target.Task; + } + catch (OperationCanceledException exception) + { + observed = exception.CancellationToken; + } + + await Assert.That(observed).IsEqualTo(token); + } + + [Test] + public async Task TaskCompletionSource_SetFromTask_CanceledWithNoToken() + { + var source = new TaskCompletionSource(); + source.TrySetCanceled(); + + var target = new TaskCompletionSource(); + await Assert.That(target.TrySetFromTask(source.Task)).IsTrue(); + + CancellationToken observed = default; + try + { + await target.Task; + } + catch (OperationCanceledException exception) + { + observed = exception.CancellationToken; + } + + await Assert.That(observed.CanBeCanceled).IsFalse(); + } + + [Test] + public async Task TaskCompletionSource_SetFromTask_Guards() + { + var completionSource = new TaskCompletionSource(); + await Assert.That(() => completionSource.SetFromTask(null!)).Throws(); + await Assert.That(() => completionSource.TrySetFromTask(null!)).Throws(); + + var generic = new TaskCompletionSource(); + await Assert.That(() => generic.SetFromTask(null!)).Throws(); + await Assert.That(() => generic.TrySetFromTask(null!)).Throws(); + + // an incomplete task is rejected by TrySetFromTask too, rather than returning false + var pending = new TaskCompletionSource(); + await Assert.That(() => completionSource.SetFromTask(pending.Task)).Throws(); + await Assert.That(() => completionSource.TrySetFromTask(pending.Task)).Throws(); + await Assert.That(() => generic.SetFromTask(pending.Task)).Throws(); + await Assert.That(() => generic.TrySetFromTask(pending.Task)).Throws(); + } + + [Test] + public async Task TaskCompletionSource_SetFromTask_AlreadyCompleted() + { + var completionSource = new TaskCompletionSource(); + completionSource.SetResult(); + + await Assert.That(() => completionSource.SetFromTask(Task.CompletedTask)).Throws(); + await Assert.That(completionSource.TrySetFromTask(Task.CompletedTask)).IsFalse(); + + var generic = new TaskCompletionSource(); + generic.SetResult(1); + + await Assert.That(() => generic.SetFromTask(Task.FromResult(2))).Throws(); + await Assert.That(generic.TrySetFromTask(Task.FromResult(2))).IsFalse(); + await Assert.That(await generic.Task).IsEqualTo(1); + } +}