1

I have a C# class that inherits from a collection. I make heavy use of LINQ, so calls to my class methods are intermingled with LINQ calls. Unfortunately this means that I frequently cast the IEnumerable<> returned by LINQ back into my class type in the middle of a pipeline. This results in excess code, and it is computationally inefficient.

Another solution is to implement the class methods as extension methods. This is more efficient, but in this case I will end up having to reproduce functionality that I could inherit from a collection.

Is there a way to inherit from collections, but still efficiently interact with LINQ?

The following program contains two queries. The first query calls a method from a derived class, but it also requires an O(n) call to ToMyList(). The second query is more efficient, but it is not making use of a derived class.

using System.Collections.Generic;
using System.Linq;

namespace StackOverflow
{
    // A custom list that can multiply every element by a constant integer.
    public class MyList : List<int>
    {
        public MyList() : base() { }
        public MyList(IEnumerable<int> items) : base(items) { }

        public MyList Multiply(int n)
        {
            for(int i = 0; i < Count; ++i)
                this[i] *= n;

            return this;
        }
    }


    public static class Extensions
    {
        // Convert from IEnumerable<int> to MyList.
        public static MyList ToMyList(this IEnumerable<int> items)
        {
            return new MyList(items);
        }


        // An extension version of the multipy method.
        public static IEnumerable<int> Multiply(this IEnumerable<int> items, int n)
        {
            foreach (var item in items)
                yield return n * item;
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            // Create a large list.
            var myList = new MyList();
            for (int i = 0; i < 1000000; ++i) myList.Add(i);

            // Call the MyList.Multiply method.
            var query1 = myList.Skip(100).ToMyList().Multiply(5);

            // Call the extension version of the Multiply method.
            var query2 = myList.Skip(100).Multiply(5);
        }
    }
}
Stephen Kennedy
  • 20,585
  • 22
  • 95
  • 108
Warren
  • 13
  • 2
  • Have you considered making your own enumerator and Linq extension methods so that it can work with the MyList type. – Scott Chamberlain Apr 02 '18 at 05:14
  • It's not really _'casting'_ if you create a whole new MyList object, and yes, it's horribly inefficient. Why do you need the non-extension method `Multiply`? Also mixing code that mutates an object with code that doesn't in a single chained method call will cause confusion to anyone else looking at this later. – Ian Mercer Apr 02 '18 at 05:20
  • Are you sure it is inefficient? If you pass a collection that supports random access (`ICollection`) into a list, it uses `ICollection.CopyTo()`, which in every case I'm aware of uses a fast `Array.Copy()` call. https://source.dot.net/#System.Private.CoreLib/shared/System/Collections/Generic/List.cs,d2ac2c19c9cf1d44,references – jdphenix Apr 02 '18 at 05:21
  • 1
    Why do you inherit a collection in the first place? [Is your class a business object or a mechanism?](https://stackoverflow.com/questions/21692193/why-not-inherit-from-listt) – Zohar Peled Apr 02 '18 at 05:24
  • @jdphenix aside from the obvious allocations issue, what if the next call in the chain is `.First()`? Mixing LINQ deferred execution with forced enumeration and object allocation really doesn't seem like a good idea. Not going to be 'least astonishment' to anyone else using it. – Ian Mercer Apr 02 '18 at 05:24
  • You appear to want to use inheritance, but really composition is often the better approach. See https://en.wikipedia.org/wiki/Composition_over_inheritance – Ian Mercer Apr 02 '18 at 05:25

1 Answers1

1

The short answer is no. On the one hand, a list (and any derivative) is necessarily managing its own copy of the data. On the other hand, Linq transforms data without storing it. So if you perform a Linq operation and you want it back in the MyList instance then you necessarily have to incur the O(N) cost of storing it.

What you could do, depending on your intended use, is modify MyList so that instead of deriving from list, it holds an enumerable, and stores transformations on that enumerable. This would allow the following:

public class MyNonList : IEnumerable<int>
{
    IEnumerable<int> _inner;

    public MyNonList(IEnumerable<int> inner)
    {
        _inner = inner;
    }

    public MyNonList Multiply(int n)
    {
        return new MyNonList(_inner.Select(i => i * n));
    }

    public IEnumerator<int> GetEnumerator() => _inner.GetEnumerator();

    IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_inner).GetEnumerator();
}


public static class Extensions
{
    // Convert from IEnumerable<int> to MyList.
    public static MyNonList ToMyNonList(this IEnumerable<int> items)
    {
        return new MyNonList(items);
    }
}


class Program
{
    static void Main(string[] args)
    {
        // Create a large list.
        var list = new List<int>();
        for (int i = 0; i < 1000000; ++i)
            list.Add(i);
        var myList = new MyNonList(list);

        // Call the MyList.Multiply method.
        MyNonList myList2 = myList.Skip(100).ToMyNonList().Multiply(5);
    }
}

But in this trivial example, this really offers very little over just implementing the static extension method. (Which, by the way is a perfectly reasonable thing to do).

You could also split MyList out to two separate classes: one that derives from List, and actually holds data; and a second in the manner here that contains the transformation methods of interest, finishing with its own ToMyList implementation that finally realises and stores the enumerable data.

Peter Aylett
  • 750
  • 3
  • 8