IList.Select()返回IList(不复制)

时间:2014-10-22 08:41:57

标签: c# .net linq collections

我有一个IList<T>参数的方法。它是IList而不是IEnumerable,因为该方法需要快速随机访问,并且根本不会查询大多数条目(算法类似于binary search),IList似乎是唯一的适合的.NET接口。

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

但现在我有以下情况:

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

所需的值不是直接在列表中,而是列表项的成员。上面的LINQ代码解决了这个问题,但有一点需要注意:整个列表都被复制了!我不希望这样,因为列表可能很大。

如何在不复制的情况下执行此类选择?有没有办法在IList上执行Select返回IList而不是IEnumerable?

到目前为止我考虑过的解决方案:

  1. 将选择器传递给DoStuff并让它即时进行选择。
  2. 编写一个包含IList和选择器的包装器,实现IList,并在查询项时进行选择。
  3. 我不喜欢(1),因为进行选择不是DoStuff的工作。现在,(2)将是我的解决方案,但我想知道是否有更好的方法可以做到这一点,甚至可能是我忽略的内置内容。

1 个答案:

答案 0 :(得分:2)

不,就我所知,框架内没有任何内容可以做到这一点。你的第二个选择对我来说似乎是明智的。你需要将它设为只读,为IList<T>的变异操作抛出适当的例外。

如果你使用.NET 4+,另一种选择是采用IReadOnlyList<T>代替 - 实现起来要简单得多,因为它没有那些你只是抛出异常的成员无论如何。它还强制你的DoStuff方法只在编译时使用“读取”操作,而不是让它试图改变列表只在执行时失败。

示例实现,完全未经测试:

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();
    }
}