返回IList(不复制)的IList.Select()
本文关键字:IList Select 返回 复制 | 更新日期: 2023-09-27 18:27:21
我有一个带有IList<T>
参数的方法。它是IList
而不是IEnumerable
,因为该方法需要快速随机访问,而且大多数条目根本不会被查询(该算法类似于二进制搜索),而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
?
到目前为止我考虑的解决方案:
- 将选择器传递给
DoStuff
,并让它进行动态选择 - 编写一个包装器,它接受一个IList和一个选择器,实现IList,并在查询项时进行选择
我不喜欢(1),因为做选择不是DoStuff
的工作。现在,(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();
}
}