IList.Select() that returns an IList (without copying)

I have a method with an IList<T> parameter. It's IList and not IEnumerable, because the method needs quick random access and most entries will not be queried at all (the algorithm is similar to binary search), and IList seems to be the only suitable .NET interface for this.

public static int DoStuff<T>(System.Collections.Generic.IList<T> list)
{
  // ...
}

But now I have something like the following situation:

System.Tuple<int, int>[] originalList = { /* ... */ };
System.Collections.Generic.IList<int> list = originalList
    .Select(x => x.Item1)
    .ToList();

The required values are not directly in the list, but are members of the list items. The above LINQ code solves the issue, but there is a caveat: The whole list gets copied! I don't want that, because the list may be huge.

How do I perform such a selection without copying? Is there a way to do a Select on an IList that returns an IList rather than an IEnumerable?

Solutions I considered so far:

  1. Pass a selector to DoStuff and let it do the selections on the fly.
  2. Write a wrapper that takes an IList and a selector, implements IList, and does the selection whenever an item is queried.

I don't like (1), because it's not the job of DoStuff to do the selection. Right now, (2) would be my solution, but I'm wondering if there is a nicer way to do that, maybe even something built-in that I overlooked.

Jon Skeet
people
quotationmark

No, there's nothing within the framework that does this as far as I'm aware. Your second option seems sensible to me. You'll need to make it read-only, throwing appropriate exceptions for the mutating operations of IList<T>.

One alternative if you're using .NET 4+ would be to take IReadOnlyList<T> instead - that would be significantly simpler to implement, as it doesn't have all those members that you'd just be throwing exceptions from anyway. It would also enforce that your DoStuff method only used "read" operations at compile-time, rather than letting it try to mutate the list only to fail at execution time.

Sample implementation, completely untested:

public static class ListViewExtensions
{
    // Used to take advantage of type inference.
    public static ListView<TSource, TTarget> ToListView<TSource, TTarget>
        (this IList<TSource> source, Func<TSource, TTarget> selector)
    {
        return new ListView<TSource, TTarget>(source, selector);
    }
}

public sealed class ListView<TSource, TTarget> : IReadOnlyList<TTarget>
{
    // Or IReadOnlyList<TSource>... it's a shame that IList<T> doesn't
    // implement IReadOnlyList<T> :(
    private readonly IList<TSource> source;

    private readonly Func<TSource, TTarget> selector;

    public ListView(IList<TSource> source, Func<TSource, TTarget> selector)
    {
        // TODO: Nullity validation
        this.source = source;
        this.selector = selector;
    }

    public int Count { get { return source.Count; } }

    public TTarget this[int index]
    {
        get { return selector(source[index]); }
    }

    public IEnumerator<TTarget> GetEnumerator()
    {
        return source.Select(selector);
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

people

See more on this question at Stackoverflow