|
19 | 19 | using System.Collections.Generic;
|
20 | 20 | using System.IO;
|
21 | 21 | using System.Linq;
|
| 22 | +using System.Threading.Tasks; |
22 | 23 |
|
23 | 24 | #if NET8_0_OR_GREATER
|
24 | 25 | using System.Buffers.Text;
|
@@ -207,5 +208,140 @@ public static Guid GuidFromBigEndian(byte[] bigEndianBytes)
|
207 | 208 | // Create the Guid from the correctly formatted mixed-endian byte array
|
208 | 209 | return new Guid(mixedEndianBytes);
|
209 | 210 | }
|
| 211 | + |
| 212 | + /// <summary> |
| 213 | + /// Asynchronously transforms each element of an IEnumerable<T>, |
| 214 | + /// passing in its zero-based index and the element itself, using a |
| 215 | + /// Func<int, object, Task<object>>, and returns a List<T> of the transformed elements. |
| 216 | + /// </summary> |
| 217 | + /// <param name="sourceEnumerable"> |
| 218 | + /// An object implementing IEnumerable<T> for some T. |
| 219 | + /// </param> |
| 220 | + /// <param name="indexedTransformer"> |
| 221 | + /// A function that takes (index, element as object) and returns a Task whose Result |
| 222 | + /// is the new element (as object). The returned object must be castable to T. |
| 223 | + /// </param> |
| 224 | + /// <returns> |
| 225 | + /// A Task whose Result is a List<T> (boxed as object) containing all transformed elements. |
| 226 | + /// Await and then cast back to IEnumerable<T> to enumerate. |
| 227 | + /// </returns> |
| 228 | + public static async Task<object> TransformEnumerableAsync( |
| 229 | + object sourceEnumerable, |
| 230 | + Func<int, object, Task<object>> indexedTransformer) |
| 231 | + { |
| 232 | + if (sourceEnumerable == null) |
| 233 | + throw new ArgumentNullException(nameof(sourceEnumerable)); |
| 234 | + if (indexedTransformer == null) |
| 235 | + throw new ArgumentNullException(nameof(indexedTransformer)); |
| 236 | + |
| 237 | + // 1. Find the IEnumerable<T> interface on the source object |
| 238 | + var srcType = sourceEnumerable.GetType(); |
| 239 | + var enumInterface = srcType |
| 240 | + .GetInterfaces() |
| 241 | + .FirstOrDefault(i => |
| 242 | + i.IsGenericType && |
| 243 | + i.GetGenericTypeDefinition() == typeof(IEnumerable<>)); |
| 244 | + |
| 245 | + if (enumInterface == null) |
| 246 | + throw new ArgumentException("Source must implement IEnumerable<T>", nameof(sourceEnumerable)); |
| 247 | + |
| 248 | + // 2. Extract the element type T |
| 249 | + var elementType = enumInterface.GetGenericArguments()[0]; |
| 250 | + |
| 251 | + // 3. Build a List<T> at runtime |
| 252 | + var listType = typeof(List<>).MakeGenericType(elementType); |
| 253 | + var resultList = (IList)Activator.CreateInstance(listType); |
| 254 | + |
| 255 | + // 4. Kick off all transforms in parallel |
| 256 | + var tasks = new List<Task<object>>(); |
| 257 | + int index = 0; |
| 258 | + foreach (var item in (IEnumerable)sourceEnumerable) |
| 259 | + { |
| 260 | + tasks.Add(indexedTransformer(index, item)); |
| 261 | + index++; |
| 262 | + } |
| 263 | + |
| 264 | + // 5. Await them all at once |
| 265 | + var results = await Task.WhenAll(tasks).ConfigureAwait(false); |
| 266 | + |
| 267 | + // 6. Populate the result list in original order |
| 268 | + foreach (var transformed in results) |
| 269 | + { |
| 270 | + resultList.Add(transformed); |
| 271 | + } |
| 272 | + |
| 273 | + // 7. Return the List<T> as object |
| 274 | + return resultList; |
| 275 | + } |
| 276 | + |
| 277 | + /// <summary> |
| 278 | + /// Asynchronously transforms each value of an IDictionary<K,V>, |
| 279 | + /// by invoking the provided Func<object, object, Task<object>> |
| 280 | + /// passing in the key and the original value |
| 281 | + /// and returns a new Dictionary<K,V> whose values are the awaited results. |
| 282 | + /// </summary> |
| 283 | + /// <param name="sourceDictionary"> |
| 284 | + /// An object implementing IDictionary<K,V> for some K,V. |
| 285 | + /// </param> |
| 286 | + /// <param name="transformer"> |
| 287 | + /// A function that takes (key as object, value as object) and returns a Task whose Result |
| 288 | + /// is the new value (as object). The returned object must be castable to V. |
| 289 | + /// </param> |
| 290 | + /// <returns> |
| 291 | + /// A Task whose Result is a Dictionary<K,V> containing all the transformed values. |
| 292 | + /// Await and then cast back to IDictionary<K,V> to enumerate. |
| 293 | + /// </returns> |
| 294 | + public static async Task<object> TransformDictionaryAsync( |
| 295 | + object sourceDictionary, |
| 296 | + Func<object, object, Task<object>> transformer) |
| 297 | + { |
| 298 | + if (sourceDictionary == null) |
| 299 | + throw new ArgumentNullException(nameof(sourceDictionary)); |
| 300 | + if (transformer == null) |
| 301 | + throw new ArgumentNullException(nameof(transformer)); |
| 302 | + |
| 303 | + // 1. Find the IDictionary<K,V> interface on the source object |
| 304 | + var srcType = sourceDictionary.GetType(); |
| 305 | + var dictInterface = srcType |
| 306 | + .GetInterfaces() |
| 307 | + .FirstOrDefault(i => |
| 308 | + i.IsGenericType && |
| 309 | + i.GetGenericTypeDefinition() == typeof(IDictionary<,>)); |
| 310 | + |
| 311 | + if (dictInterface == null) |
| 312 | + throw new ArgumentException("Source must implement IDictionary<K,V>", nameof(sourceDictionary)); |
| 313 | + |
| 314 | + // 2. Extract K and V |
| 315 | + var genericArgs = dictInterface.GetGenericArguments(); |
| 316 | + var keyType = genericArgs[0]; |
| 317 | + var valueType = genericArgs[1]; |
| 318 | + |
| 319 | + // 3. Create a Dictionary<K,V> at runtime |
| 320 | + var resultDictType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); |
| 321 | + var resultDict = (IDictionary)Activator.CreateInstance(resultDictType); |
| 322 | + |
| 323 | + // 4. Enumerate the source via the non‐generic IDictionary interface |
| 324 | + var nonGenericDict = (IDictionary)sourceDictionary; |
| 325 | + var keys = new List<object>(); |
| 326 | + var tasks = new List<Task<object>>(); |
| 327 | + |
| 328 | + foreach (DictionaryEntry entry in nonGenericDict) |
| 329 | + { |
| 330 | + keys.Add(entry.Key); |
| 331 | + tasks.Add(transformer(entry.Key, entry.Value)); |
| 332 | + } |
| 333 | + |
| 334 | + // 5. Await all transformations |
| 335 | + var transformedValues = await Task.WhenAll(tasks).ConfigureAwait(false); |
| 336 | + |
| 337 | + // 6. Reconstruct the new dictionary in original order |
| 338 | + for (int i = 0; i < keys.Count; i++) |
| 339 | + { |
| 340 | + resultDict.Add(keys[i], transformedValues[i]); |
| 341 | + } |
| 342 | + |
| 343 | + // 7. Return boxed Dictionary<K,V> |
| 344 | + return resultDict; |
| 345 | + } |
210 | 346 | }
|
211 | 347 | }
|
0 commit comments