【问题标题】:IList.Select() that returns an IList (without copying)IList.Select() 返回 IList(不复制)
【发布时间】:2014-10-22 08:41:56
【问题描述】:

我有一个带有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 而不是 IEnumerable 的 IList 上执行 Select

目前我考虑的解决方案:

  1. 将选择器传递给DoStuff,让它即时进行选择。
  2. 编写一个接受 IList 和选择器的包装器,实现 IList,并在查询项目时进行选择。

我不喜欢 (1),因为选择不是 DoStuff 的工作。现在,(2)将是我的解决方案,但我想知道是否有更好的方法可以做到这一点,甚至可能是我忽略的内置方法。

【问题讨论】:

    标签: c# .net linq collections


    【解决方案1】:

    不,据我所知,框架内没有任何东西可以做到这一点。你的第二个选择对我来说似乎是明智的。您需要将其设为只读,为IList&lt;T&gt; 的变异操作引发适当的异常。

    如果您使用 .NET 4+,另一种选择是采用 IReadOnlyList&lt;T&gt; 代替 - 这将更容易实现,因为它没有所有那些您无论如何都会抛出异常的成员.它还会强制您的 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();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-07-03
      • 2019-11-23
      • 1970-01-01
      • 2011-09-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-13
      相关资源
      最近更新 更多