我有一个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?
到目前为止我考虑过的解决方案:
DoStuff
并让它即时进行选择。我不喜欢(1),因为进行选择不是DoStuff
的工作。现在,(2)将是我的解决方案,但我想知道是否有更好的方法可以做到这一点,甚至可能是我忽略的内置内容。
答案 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();
}
}