Home General

.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 structs, 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

  • 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);
            }
        }
    }
    
Sign In or Register to comment.