.Dump() and the ZLinq library
Hi everyone,
I'm investigating the ZLinq, a highly optimized Linq implementation, which aims at reducing allocations.
I tried it with LinqPad, but the normal .Dump()
does not work out of the box. For dotnet 9, ZLinq uses ref struct
s, which makes calling Dump
fail for most scenarios.
I've created a custom "overload" to handle ValueEnumerable
results:
public static class DumpExtensions { public static ValueEnumerable<TEnumerator, T> Dump<TEnumerator, T>(this ValueEnumerable<TEnumerator, T> valueEnumerable) where TEnumerator : struct, IValueEnumerator<T> // Comment out next line for dotnet < 9 , allows ref struct { if (valueEnumerable.Enumerator.TryGetSpan(out var span)) { // Special case handling, if a Span can be accessed span.Dump(); } else { if (valueEnumerable.TryGetNonEnumeratedCount(out var count)) { // Todo: use count for displaying total number of items } var maxRows = Util.DumpDefaults.MaxRows ?? 1000; using var pooledArray = valueEnumerable .Take(maxRows) .ToArrayPool(); pooledArray.ArraySegment.Dump(); } return valueEnumerable; } }
This seems to work for me so far. Is anyone else interested in this and does it make sense to extend it?
There are some issues, for example the type is not displayed correctly. I'd like to have ValueEnumerable<Int32> (First 1000 items of 10000)
instead of ArraySegment<Int32> (10 items)
. But I couldn't figure out a way to achieve this. Any ideas?
Kind regards
Peter
Comments
-
Might be related to this one.
-
How about this:
public static class ZLinqDumpExtensions { public static ValueEnumerable<TEnumerator, T> Dump<TEnumerator, T> ( this ValueEnumerable<TEnumerator, T> valueEnumerable ) where TEnumerator : struct, IValueEnumerator<T> #if NET9 , allows ref struct #endif { if (valueEnumerable.Enumerator.TryGetSpan(out var span)) { // Special case handling, if a Span can be accessed span.Dump(); } else { var maxRows = Util.DumpDefaults.MaxRows ?? 1000; using var pooledArray = valueEnumerable .Take(maxRows) .ToArrayPool(); if (valueEnumerable.TryGetNonEnumeratedCount(out var count)) new Countable.ValueEnumerable<T>(pooledArray.ArraySegment, count).Dump(); else new ValueEnumerable<T>(pooledArray.ArraySegment).Dump(); } return valueEnumerable; } public class ValueEnumerable<T> : IEnumerable<T> { IEnumerable<T> _inner; public ValueEnumerable(IEnumerable<T> valueEnumerable) { _inner = valueEnumerable; } // Append a blank item to the end, so LINQPad returns "First x items" rather than "x items". public IEnumerator<T> GetEnumerator() => _inner.Append(default(T)).GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class Countable { public class ValueEnumerable<T> : ICollection, ICollection<T> { ICollection<T> _inner; int _count; public ValueEnumerable(ICollection<T> valueEnumerable, int count) { _inner = valueEnumerable; _count = count; } int ICollection.Count => _count; int ICollection<T>.Count => _count; bool ICollection.IsSynchronized => false; object ICollection.SyncRoot => this; bool ICollection<T>.IsReadOnly => true; public IEnumerator<T> GetEnumerator() => _inner.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_inner).GetEnumerator(); void ICollection<T>.Add(T item) => throw new NotSupportedException(); void ICollection<T>.Clear() => throw new NotSupportedException(); bool ICollection<T>.Remove(T item) => throw new NotSupportedException(); bool ICollection<T>.Contains(T item) => _inner.Contains(item); void ICollection.CopyTo(Array array, int index) => ((ICollection)_inner).CopyTo(array, index); void ICollection<T>.CopyTo(T[] array, int arrayIndex) => _inner.CopyTo(array, arrayIndex); } } }