11#if NET6_0_OR_GREATER
22using System ;
33using System . Collections . Generic ;
4- using System . Linq ;
54using System . Runtime . CompilerServices ;
65using System . Threading ;
76using System . Threading . Tasks ;
@@ -29,19 +28,19 @@ public static async Task RunExamples()
2928 // Example 1: The problematic pattern from the issue
3029 Console . WriteLine ( "Example 1: PROBLEMATIC - No disposal (resource leak!)" ) ;
3130 var results1 = await ProblematicPatternAsync ( new [ ] { 1 , 2 , 3 , 4 , 5 } , CancellationToken . None ) ;
32- Console . WriteLine ( $ "Results: { string . Join ( ", " , results1 . ToList ( ) ) } ") ;
31+ Console . WriteLine ( $ "Results: { string . Join ( ", " , results1 ) } ") ;
3332 Console . WriteLine ( "⚠️ This pattern leaks resources because the processor is never disposed!\n " ) ;
3433
3534 // Example 2: Proper disposal with await using
3635 Console . WriteLine ( "Example 2: PROPER - Using await using for automatic disposal" ) ;
3736 var results2 = await ProperPatternWithAwaitUsingAsync ( new [ ] { 1 , 2 , 3 , 4 , 5 } , CancellationToken . None ) ;
38- Console . WriteLine ( $ "Results: { string . Join ( ", " , results2 . ToList ( ) ) } ") ;
37+ Console . WriteLine ( $ "Results: { string . Join ( ", " , results2 ) } ") ;
3938 Console . WriteLine ( "✅ Resources automatically cleaned up with await using\n " ) ;
4039
4140 // Example 3: Proper disposal with manual try-finally
4241 Console . WriteLine ( "Example 3: PROPER - Manual disposal with try-finally" ) ;
4342 var results3 = await ProperPatternWithManualDisposalAsync ( new [ ] { 1 , 2 , 3 , 4 , 5 } , CancellationToken . None ) ;
44- Console . WriteLine ( $ "Results: { string . Join ( ", " , results3 . ToList ( ) ) } ") ;
43+ Console . WriteLine ( $ "Results: { string . Join ( ", " , results3 ) } ") ;
4544 Console . WriteLine ( "✅ Resources manually cleaned up in finally block\n " ) ;
4645
4746 // Example 4: Using the convenience extension (no disposal needed)
@@ -65,23 +64,27 @@ public static async Task RunExamples()
6564 /// This is the PROBLEMATIC pattern from the GitHub issue - it leaks resources!
6665 /// DO NOT USE THIS PATTERN in production code.
6766 /// </summary>
68- private static async Task < IAsyncEnumerable < int > > ProblematicPatternAsync ( int [ ] input , CancellationToken token )
67+ private static async Task < IReadOnlyList < int > > ProblematicPatternAsync ( int [ ] input , CancellationToken token )
6968 {
7069 // ⚠️ PROBLEM: The processor is created but never disposed!
7170 var batchProcessor = input . SelectAsync ( static v => TransformAsync ( v ) , token ) . ProcessInParallel ( ) ;
72-
73- // This returns the async enumerable, but the processor that created it is never disposed
74- return batchProcessor . GetResultsAsyncEnumerable ( ) ;
75-
71+
72+ var results = new List < int > ( ) ;
73+ await foreach ( var result in batchProcessor . GetResultsAsyncEnumerable ( ) )
74+ {
75+ results . Add ( result ) ;
76+ }
77+
7678 // 🔥 RESOURCE LEAK: The processor goes out of scope without being disposed,
7779 // potentially leaving tasks running and resources uncleaned
80+ return results ;
7881 }
7982
8083 /// <summary>
8184 /// PROPER pattern using await using for automatic disposal.
8285 /// This is the recommended approach.
8386 /// </summary>
84- private static async Task < IAsyncEnumerable < int > > ProperPatternWithAwaitUsingAsync ( int [ ] input , CancellationToken token )
87+ private static async Task < IReadOnlyList < int > > ProperPatternWithAwaitUsingAsync ( int [ ] input , CancellationToken token )
8588 {
8689 // ✅ Create processor with await using for automatic disposal
8790 await using var processor = input . SelectAsync ( static v => TransformAsync ( v ) , token ) . ProcessInParallel ( ) ;
@@ -93,17 +96,15 @@ private static async Task<IAsyncEnumerable<int>> ProperPatternWithAwaitUsingAsyn
9396 results . Add ( result ) ;
9497 }
9598
96- // Return as async enumerable
97- return results . ToAsyncEnumerable ( ) ;
98-
9999 // ✅ Processor is automatically disposed here due to 'await using'
100+ return results ;
100101 }
101102
102103 /// <summary>
103104 /// PROPER pattern using manual disposal with try-finally.
104105 /// Use this when you need more control over the disposal timing.
105106 /// </summary>
106- private static async Task < IAsyncEnumerable < int > > ProperPatternWithManualDisposalAsync ( int [ ] input , CancellationToken token )
107+ private static async Task < IReadOnlyList < int > > ProperPatternWithManualDisposalAsync ( int [ ] input , CancellationToken token )
107108 {
108109 var processor = input . SelectAsync ( static v => TransformAsync ( v ) , token ) . ProcessInParallel ( ) ;
109110
@@ -116,7 +117,7 @@ private static async Task<IAsyncEnumerable<int>> ProperPatternWithManualDisposal
116117 results . Add ( result ) ;
117118 }
118119
119- return results . ToAsyncEnumerable ( ) ;
120+ return results ;
120121 }
121122 finally
122123 {
@@ -168,4 +169,4 @@ private static async IAsyncEnumerable<int> GenerateAsyncEnumerable(
168169 }
169170 }
170171}
171- #endif
172+ #endif
0 commit comments