@@ -382,8 +382,61 @@ public static bool TryGetMemoryManager<T, TManager>(ReadOnlyMemory<T> memory, [N
382382 /// <returns>An <see cref="IEnumerable{T}"/> view of the given <paramref name="memory" /></returns>
383383 public static IEnumerable < T > ToEnumerable < T > ( ReadOnlyMemory < T > memory )
384384 {
385- for ( int i = 0 ; i < memory . Length ; i ++ )
386- yield return memory . Span [ i ] ;
385+ object ? obj = memory . GetObjectStartLength ( out int index , out int length ) ;
386+
387+ // If the memory is empty, just return an empty array as the enumerable.
388+ if ( length is 0 || obj is null )
389+ {
390+ return Array . Empty < T > ( ) ;
391+ }
392+
393+ // If the object is a string, we can optimize. If it isn't a slice, just return the string as the
394+ // enumerable. Otherwise, return an iterator dedicated to enumerating the object; while we could
395+ // use the general one for any ReadOnlyMemory, that will incur a .Span access for every element.
396+ if ( typeof ( T ) == typeof ( char ) && obj is string str )
397+ {
398+ return ( IEnumerable < T > ) ( object ) ( index == 0 && length == str . Length ?
399+ str :
400+ FromString ( str , index , length ) ) ;
401+
402+ static IEnumerable < char > FromString ( string s , int offset , int count )
403+ {
404+ for ( int i = 0 ; i < count ; i ++ )
405+ {
406+ yield return s [ offset + i ] ;
407+ }
408+ }
409+ }
410+
411+ // If the object is an array, we can optimize. If it isn't a slice, just return the array as the
412+ // enumerable. Otherwise, return an iterator dedicated to enumerating the object.
413+ if ( RuntimeHelpers . ObjectHasComponentSize ( obj ) ) // Same check as in TryGetArray to confirm that obj is a T[] or a U[] which is blittable to a T[].
414+ {
415+ T [ ] array = Unsafe . As < T [ ] > ( obj ) ;
416+ index &= ReadOnlyMemory < T > . RemoveFlagsBitMask ; // the array may be prepinned, so remove the high bit from the start index in the line below.
417+ return index == 0 && length == array . Length ?
418+ array :
419+ FromArray ( array , index , length ) ;
420+
421+ static IEnumerable < T > FromArray ( T [ ] array , int offset , int count )
422+ {
423+ for ( int i = 0 ; i < count ; i ++ )
424+ {
425+ yield return array [ offset + i ] ;
426+ }
427+ }
428+ }
429+
430+ // The ROM<T> wraps a MemoryManager<T>. The best we can do is iterate, accessing .Span on each MoveNext.
431+ return FromMemoryManager ( memory ) ;
432+
433+ static IEnumerable < T > FromMemoryManager ( ReadOnlyMemory < T > memory )
434+ {
435+ for ( int i = 0 ; i < memory . Length ; i ++ )
436+ {
437+ yield return memory . Span [ i ] ;
438+ }
439+ }
387440 }
388441
389442 /// <summary>Attempts to get the underlying <see cref="string"/> from a <see cref="ReadOnlyMemory{T}"/>.</summary>
0 commit comments