@@ -92,4 +92,68 @@ public static async Task<TResult[]> SelectAsync<TArg, TResult>(this IEnumerable<
9292
9393 return partitionResults . ToArray ( ) ;
9494 }
95+
96+ /// <summary>
97+ /// Hand-rolled to avoid depending on <c>System.Linq.AsyncEnumerable</c>, which only ships as a 10.x
98+ /// package and so transitively forces Microsoft.Bcl.AsyncInterfaces 10.0.0.0 into the Vsix output —
99+ /// a version Visual Studio 17.x cannot bind to. See <c>VsixAssemblyCompatibilityTests</c>.
100+ /// </summary>
101+ /// <remarks>
102+ /// Deliberately not named <c>ToArrayAsync</c> so it does not clash with
103+ /// <see cref="System.Linq.AsyncEnumerable"/>.<c>ToArrayAsync</c> on .NET 10+ when this assembly is
104+ /// referenced by a project whose target framework provides the BCL version.
105+ /// </remarks>
106+ public static async Task < TSource [ ] > ToArraySafeAsync < TSource > ( this IAsyncEnumerable < TSource > source ,
107+ CancellationToken cancellationToken = default )
108+ {
109+ var list = new List < TSource > ( ) ;
110+ await foreach ( var item in source . WithCancellation ( cancellationToken ) ) {
111+ list . Add ( item ) ;
112+ }
113+ return list . ToArray ( ) ;
114+ }
115+
116+ /// <summary>
117+ /// Adapts a synchronous sequence to <see cref="IAsyncEnumerable{T}"/>. Hand-rolled for the same
118+ /// reason as <see cref="ToArraySafeAsync{TSource}"/>, and likewise renamed to avoid clashing with
119+ /// <see cref="System.Linq.AsyncEnumerable"/>.<c>ToAsyncEnumerable</c> on .NET 10+.
120+ /// </summary>
121+ #pragma warning disable 1998 // async method without await; required for the iterator to compile to IAsyncEnumerable.
122+ #pragma warning disable VSTHRD200 // The method returns IAsyncEnumerable, not a Task; "Async" suffix would be misleading.
123+ public static async IAsyncEnumerable < TSource > AsAsyncEnumerable < TSource > ( this IEnumerable < TSource > source )
124+ {
125+ foreach ( var item in source ) {
126+ yield return item ;
127+ }
128+ }
129+ #pragma warning restore 1998
130+
131+ /// <summary>
132+ /// Lazy projection over an <see cref="IAsyncEnumerable{T}"/>. Hand-rolled for the same reason
133+ /// as <see cref="ToArraySafeAsync{TSource}"/>, and renamed to avoid clashing with
134+ /// <see cref="System.Linq.AsyncEnumerable"/>.<c>Select</c> on .NET 10+.
135+ /// </summary>
136+ public static async IAsyncEnumerable < TResult > SelectSafe < TSource , TResult > ( this IAsyncEnumerable < TSource > source ,
137+ Func < TSource , TResult > selector ,
138+ [ EnumeratorCancellation ] CancellationToken cancellationToken = default )
139+ {
140+ await foreach ( var item in source . WithCancellation ( cancellationToken ) ) {
141+ yield return selector ( item ) ;
142+ }
143+ }
144+
145+ /// <summary>
146+ /// Lazy projection (with index) over an <see cref="IAsyncEnumerable{T}"/>. Hand-rolled for the same
147+ /// reason as <see cref="ToArraySafeAsync{TSource}"/>.
148+ /// </summary>
149+ public static async IAsyncEnumerable < TResult > SelectSafe < TSource , TResult > ( this IAsyncEnumerable < TSource > source ,
150+ Func < TSource , int , TResult > selector ,
151+ [ EnumeratorCancellation ] CancellationToken cancellationToken = default )
152+ {
153+ var index = 0 ;
154+ await foreach ( var item in source . WithCancellation ( cancellationToken ) ) {
155+ yield return selector ( item , index ++ ) ;
156+ }
157+ }
158+ #pragma warning restore VSTHRD200
95159}
0 commit comments